Ignore ui/target-feature-gate on sparc, sparc64, powerpc, powerpc64 and powerpc64le
The test ui/target-feature-gate is not applicable on sparc, sparc64, powerpc, powerpc64 and powerpc64le and consequently fails there. So just ignore it on these targets.
use core::marker::{Unpin, Unsize};
use core::mem;
use core::pin::Pin;
-use core::ops::{CoerceUnsized, DispatchFromDyn, Deref, DerefMut, Generator, GeneratorState};
+use core::ops::{
+ CoerceUnsized, DispatchFromDyn, Deref, DerefMut, Receiver, Generator, GeneratorState
+};
use core::ptr::{self, NonNull, Unique};
use core::task::{LocalWaker, Poll};
}
}
+#[unstable(feature = "receiver_trait", issue = "0")]
+impl<T: ?Sized> Receiver for Box<T> {}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl<I: Iterator + ?Sized> Iterator for Box<I> {
type Item = I::Item;
self.truncate(new_len);
}
}
+
+ /// Rotates the double-ended queue `mid` places to the left.
+ ///
+ /// Equivalently,
+ /// - Rotates item `mid` into the first position.
+ /// - Pops the first `mid` items and pushes them to the end.
+ /// - Rotates `len() - mid` places to the right.
+ ///
+ /// # Panics
+ ///
+ /// If `mid` is greater than `len()`. Note that `mid == len()`
+ /// does _not_ panic and is a no-op rotation.
+ ///
+ /// # Complexity
+ ///
+ /// Takes `O(min(mid, len() - mid))` time and no extra space.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(vecdeque_rotate)]
+ ///
+ /// use std::collections::VecDeque;
+ ///
+ /// let mut buf: VecDeque<_> = (0..10).collect();
+ ///
+ /// buf.rotate_left(3);
+ /// assert_eq!(buf, [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]);
+ ///
+ /// for i in 1..10 {
+ /// assert_eq!(i * 3 % 10, buf[0]);
+ /// buf.rotate_left(3);
+ /// }
+ /// assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ /// ```
+ #[unstable(feature = "vecdeque_rotate", issue = "56686")]
+ pub fn rotate_left(&mut self, mid: usize) {
+ assert!(mid <= self.len());
+ let k = self.len() - mid;
+ if mid <= k {
+ unsafe { self.rotate_left_inner(mid) }
+ } else {
+ unsafe { self.rotate_right_inner(k) }
+ }
+ }
+
+ /// Rotates the double-ended queue `k` places to the right.
+ ///
+ /// Equivalently,
+ /// - Rotates the first item into position `k`.
+ /// - Pops the last `k` items and pushes them to the front.
+ /// - Rotates `len() - k` places to the left.
+ ///
+ /// # Panics
+ ///
+ /// If `k` is greater than `len()`. Note that `k == len()`
+ /// does _not_ panic and is a no-op rotation.
+ ///
+ /// # Complexity
+ ///
+ /// Takes `O(min(k, len() - k))` time and no extra space.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(vecdeque_rotate)]
+ ///
+ /// use std::collections::VecDeque;
+ ///
+ /// let mut buf: VecDeque<_> = (0..10).collect();
+ ///
+ /// buf.rotate_right(3);
+ /// assert_eq!(buf, [7, 8, 9, 0, 1, 2, 3, 4, 5, 6]);
+ ///
+ /// for i in 1..10 {
+ /// assert_eq!(0, buf[i * 3 % 10]);
+ /// buf.rotate_right(3);
+ /// }
+ /// assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ /// ```
+ #[unstable(feature = "vecdeque_rotate", issue = "56686")]
+ pub fn rotate_right(&mut self, k: usize) {
+ assert!(k <= self.len());
+ let mid = self.len() - k;
+ if k <= mid {
+ unsafe { self.rotate_right_inner(k) }
+ } else {
+ unsafe { self.rotate_left_inner(mid) }
+ }
+ }
+
+ // Safety: the following two methods require that the rotation amount
+ // be less than half the length of the deque.
+ //
+ // `wrap_copy` requres that `min(x, cap() - x) + copy_len <= cap()`,
+ // but than `min` is never more than half the capacity, regardless of x,
+ // so it's sound to call here because we're calling with something
+ // less than half the length, which is never above half the capacity.
+
+ unsafe fn rotate_left_inner(&mut self, mid: usize) {
+ debug_assert!(mid * 2 <= self.len());
+ self.wrap_copy(self.head, self.tail, mid);
+ self.head = self.wrap_add(self.head, mid);
+ self.tail = self.wrap_add(self.tail, mid);
+ }
+
+ unsafe fn rotate_right_inner(&mut self, k: usize) {
+ debug_assert!(k * 2 <= self.len());
+ self.head = self.wrap_sub(self.head, k);
+ self.tail = self.wrap_sub(self.tail, k);
+ self.wrap_copy(self.tail, self.head, k);
+ }
}
impl<T: Clone> VecDeque<T> {
#![feature(ptr_internals)]
#![feature(ptr_offset_from)]
#![feature(rustc_attrs)]
+#![feature(receiver_trait)]
#![feature(specialization)]
#![feature(split_ascii_whitespace)]
#![feature(staged_api)]
use core::marker;
use core::marker::{Unpin, Unsize, PhantomData};
use core::mem::{self, align_of_val, forget, size_of_val};
-use core::ops::Deref;
+use core::ops::{Deref, Receiver};
use core::ops::{CoerceUnsized, DispatchFromDyn};
use core::pin::Pin;
use core::ptr::{self, NonNull};
}
}
+#[unstable(feature = "receiver_trait", issue = "0")]
+impl<T: ?Sized> Receiver for Rc<T> {}
+
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc<T> {
/// Drops the `Rc`.
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::{Deref, Receiver};
use core::ops::{CoerceUnsized, DispatchFromDyn};
use core::pin::Pin;
use core::ptr::{self, NonNull};
}
}
+#[unstable(feature = "receiver_trait", issue = "0")]
+impl<T: ?Sized> Receiver for Arc<T> {}
+
impl<T: Clone> Arc<T> {
/// Makes a mutable reference into the given `Arc`.
///
#![feature(drain_filter)]
#![feature(exact_size_is_empty)]
#![feature(pattern)]
+#![feature(repeat_generic_slice)]
#![feature(slice_sort_by_cached_key)]
#![feature(str_escape)]
#![feature(try_reserve)]
#![feature(unboxed_closures)]
-#![feature(repeat_generic_slice)]
+#![feature(vecdeque_rotate)]
extern crate core;
extern crate rand;
}
}
+
+#[test]
+fn test_rotate_nop() {
+ let mut v: VecDeque<_> = (0..10).collect();
+ assert_unchanged(&v);
+
+ v.rotate_left(0);
+ assert_unchanged(&v);
+
+ v.rotate_left(10);
+ assert_unchanged(&v);
+
+ v.rotate_right(0);
+ assert_unchanged(&v);
+
+ v.rotate_right(10);
+ assert_unchanged(&v);
+
+ v.rotate_left(3);
+ v.rotate_right(3);
+ assert_unchanged(&v);
+
+ v.rotate_right(3);
+ v.rotate_left(3);
+ assert_unchanged(&v);
+
+ v.rotate_left(6);
+ v.rotate_right(6);
+ assert_unchanged(&v);
+
+ v.rotate_right(6);
+ v.rotate_left(6);
+ assert_unchanged(&v);
+
+ v.rotate_left(3);
+ v.rotate_left(7);
+ assert_unchanged(&v);
+
+ v.rotate_right(4);
+ v.rotate_right(6);
+ assert_unchanged(&v);
+
+ v.rotate_left(1);
+ v.rotate_left(2);
+ v.rotate_left(3);
+ v.rotate_left(4);
+ assert_unchanged(&v);
+
+ v.rotate_right(1);
+ v.rotate_right(2);
+ v.rotate_right(3);
+ v.rotate_right(4);
+ assert_unchanged(&v);
+
+ fn assert_unchanged(v: &VecDeque<i32>) {
+ assert_eq!(v, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ }
+}
+
+#[test]
+fn test_rotate_left_parts() {
+ let mut v: VecDeque<_> = (1..=7).collect();
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[3, 4, 5, 6, 7, 1][..], &[2][..]));
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[5, 6, 7, 1][..], &[2, 3, 4][..]));
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[7, 1][..], &[2, 3, 4, 5, 6][..]));
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[2, 3, 4, 5, 6, 7, 1][..], &[][..]));
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[4, 5, 6, 7, 1, 2][..], &[3][..]));
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[6, 7, 1, 2][..], &[3, 4, 5][..]));
+ v.rotate_left(2);
+ assert_eq!(v.as_slices(), (&[1, 2][..], &[3, 4, 5, 6, 7][..]));
+}
+
+#[test]
+fn test_rotate_right_parts() {
+ let mut v: VecDeque<_> = (1..=7).collect();
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[6, 7][..], &[1, 2, 3, 4, 5][..]));
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[4, 5, 6, 7][..], &[1, 2, 3][..]));
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[2, 3, 4, 5, 6, 7][..], &[1][..]));
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[7, 1, 2, 3, 4, 5, 6][..], &[][..]));
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[5, 6][..], &[7, 1, 2, 3, 4][..]));
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[3, 4, 5, 6][..], &[7, 1, 2][..]));
+ v.rotate_right(2);
+ assert_eq!(v.as_slices(), (&[1, 2, 3, 4, 5, 6][..], &[7][..]));
+}
+
+#[test]
+fn test_rotate_left_random() {
+ let shifts = [
+ 6, 1, 0, 11, 12, 1, 11, 7, 9, 3, 6, 1,
+ 4, 0, 5, 1, 3, 1, 12, 8, 3, 1, 11, 11,
+ 9, 4, 12, 3, 12, 9, 11, 1, 7, 9, 7, 2,
+ ];
+ let n = 12;
+ let mut v: VecDeque<_> = (0..n).collect();
+ let mut total_shift = 0;
+ for shift in shifts.iter().cloned() {
+ v.rotate_left(shift);
+ total_shift += shift;
+ for i in 0..n {
+ assert_eq!(v[i], (i + total_shift) % n);
+ }
+ }
+}
+
+#[test]
+fn test_rotate_right_random() {
+ let shifts = [
+ 6, 1, 0, 11, 12, 1, 11, 7, 9, 3, 6, 1,
+ 4, 0, 5, 1, 3, 1, 12, 8, 3, 1, 11, 11,
+ 9, 4, 12, 3, 12, 9, 11, 1, 7, 9, 7, 2,
+ ];
+ let n = 12;
+ let mut v: VecDeque<_> = (0..n).collect();
+ let mut total_shift = 0;
+ for shift in shifts.iter().cloned() {
+ v.rotate_right(shift);
+ total_shift += shift;
+ for i in 0..n {
+ assert_eq!(v[(i + total_shift) % n], i);
+ }
+ }
+}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+ #[inline]
+ fn nth(&mut self, n: usize) -> Option<<I as Iterator>::Item> { self.iter.nth_back(n) }
+
fn try_fold<B, F, R>(&mut self, init: B, f: F) -> R where
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
{
#[inline]
fn next_back(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next() }
+ #[inline]
+ fn nth_back(&mut self, n: usize) -> Option<<I as Iterator>::Item> { self.iter.nth(n) }
+
fn try_rfold<B, F, R>(&mut self, init: B, f: F) -> R where
Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
{
#[stable(feature = "rust1", since = "1.0.0")]
fn next_back(&mut self) -> Option<Self::Item>;
+ /// Returns the `n`th element from the end of the iterator.
+ ///
+ /// This is essentially the reversed version of [`nth`]. Although like most indexing
+ /// operations, the count starts from zero, so `nth_back(0)` returns the first value fro
+ /// the end, `nth_back(1)` the second, and so on.
+ ///
+ /// Note that all elements between the end and the returned element will be
+ /// consumed, including the returned element. This also means that calling
+ /// `nth_back(0)` multiple times on the same iterator will return different
+ /// elements.
+ ///
+ /// `nth_back()` will return [`None`] if `n` is greater than or equal to the length of the
+ /// iterator.
+ ///
+ /// [`None`]: ../../std/option/enum.Option.html#variant.None
+ /// [`nth`]: ../../std/iter/trait.Iterator.html#method.nth
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(iter_nth_back)]
+ /// let a = [1, 2, 3];
+ /// assert_eq!(a.iter().nth_back(2), Some(&1));
+ /// ```
+ ///
+ /// Calling `nth_back()` multiple times doesn't rewind the iterator:
+ ///
+ /// ```
+ /// #![feature(iter_nth_back)]
+ /// let a = [1, 2, 3];
+ ///
+ /// let mut iter = a.iter();
+ ///
+ /// assert_eq!(iter.nth_back(1), Some(&2));
+ /// assert_eq!(iter.nth_back(1), None);
+ /// ```
+ ///
+ /// Returning `None` if there are less than `n + 1` elements:
+ ///
+ /// ```
+ /// #![feature(iter_nth_back)]
+ /// let a = [1, 2, 3];
+ /// assert_eq!(a.iter().nth_back(10), None);
+ /// ```
+ #[inline]
+ #[unstable(feature = "iter_nth_back", issue = "56995")]
+ fn nth_back(&mut self, mut n: usize) -> Option<Self::Item> {
+ for x in self.rev() {
+ if n == 0 { return Some(x) }
+ n -= 1;
+ }
+ None
+ }
+
/// This is the reverse version of [`try_fold()`]: it takes elements
/// starting from the back of the iterator.
///
/// ```
#[inline]
#[stable(feature = "iterator_try_fold", since = "1.27.0")]
- fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R where
- Self: Sized, F: FnMut(B, Self::Item) -> R, R: Try<Ok=B>
+ fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
+ where
+ Self: Sized,
+ F: FnMut(B, Self::Item) -> R,
+ R: Try<Ok=B>
{
let mut accum = init;
while let Some(x) = self.next_back() {
/// ```
#[inline]
#[stable(feature = "iter_rfold", since = "1.27.0")]
- fn rfold<B, F>(mut self, accum: B, mut f: F) -> B where
- Self: Sized, F: FnMut(B, Self::Item) -> B,
+ fn rfold<B, F>(mut self, accum: B, mut f: F) -> B
+ where
+ Self: Sized,
+ F: FnMut(B, Self::Item) -> B,
{
self.try_rfold(accum, move |acc, x| Ok::<B, !>(f(acc, x))).unwrap()
}
/// ```
#[inline]
#[stable(feature = "iter_rfind", since = "1.27.0")]
- fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item> where
+ fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
+ where
Self: Sized,
P: FnMut(&Self::Item) -> bool
{
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, I: DoubleEndedIterator + ?Sized> DoubleEndedIterator for &'a mut I {
- fn next_back(&mut self) -> Option<I::Item> { (**self).next_back() }
+ fn next_back(&mut self) -> Option<I::Item> {
+ (**self).next_back()
+ }
+ fn nth_back(&mut self, n: usize) -> Option<I::Item> {
+ (**self).nth_back(n)
+ }
}
/// An iterator that knows its exact length.
impl<T: ?Sized> DerefMut for &mut T {
fn deref_mut(&mut self) -> &mut T { *self }
}
+
+/// Indicates that a struct can be used as a method receiver, without the
+/// `arbitrary_self_types` feature. This is implemented by stdlib pointer types like `Box<T>`,
+/// `Rc<T>`, `&T`, and `Pin<P>`.
+#[cfg_attr(not(stage0), lang = "receiver")]
+#[unstable(feature = "receiver_trait", issue = "0")]
+#[doc(hidden)]
+pub trait Receiver {
+ // Empty.
+}
+
+#[unstable(feature = "receiver_trait", issue = "0")]
+impl<T: ?Sized> Receiver for &T {}
+
+#[unstable(feature = "receiver_trait", issue = "0")]
+impl<T: ?Sized> Receiver for &mut T {}
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::deref::{Deref, DerefMut};
+#[unstable(feature = "receiver_trait", issue = "0")]
+pub use self::deref::Receiver;
+
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::drop::Drop;
use fmt;
use marker::Sized;
-use ops::{Deref, DerefMut, CoerceUnsized, DispatchFromDyn};
+use ops::{Deref, DerefMut, Receiver, CoerceUnsized, DispatchFromDyn};
#[doc(inline)]
pub use marker::Unpin;
}
}
+#[unstable(feature = "receiver_trait", issue = "0")]
+impl<P: Receiver> Receiver for Pin<P> {}
+
#[unstable(feature = "pin", issue = "49150")]
impl<P: fmt::Debug> fmt::Debug for Pin<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
assert_eq!(v.iter().nth(v.len()), None);
}
+#[test]
+fn test_iterator_nth_back() {
+ let v: &[_] = &[0, 1, 2, 3, 4];
+ for i in 0..v.len() {
+ assert_eq!(v.iter().nth_back(i).unwrap(), &v[v.len() - 1 - i]);
+ }
+ assert_eq!(v.iter().nth_back(v.len()), None);
+}
+
+#[test]
+fn test_iterator_rev_nth_back() {
+ let v: &[_] = &[0, 1, 2, 3, 4];
+ for i in 0..v.len() {
+ assert_eq!(v.iter().rev().nth_back(i).unwrap(), &v[i]);
+ }
+ assert_eq!(v.iter().rev().nth_back(v.len()), None);
+}
+
+#[test]
+fn test_iterator_rev_nth() {
+ let v: &[_] = &[0, 1, 2, 3, 4];
+ for i in 0..v.len() {
+ assert_eq!(v.iter().rev().nth(i).unwrap(), &v[v.len() - 1 - i]);
+ }
+ assert_eq!(v.iter().rev().nth(v.len()), None);
+}
+
#[test]
fn test_iterator_last() {
let v: &[_] = &[0, 1, 2, 3, 4];
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(hashmap_internals)]
+#![feature(iter_nth_back)]
#![feature(iter_unfold)]
#![feature(pattern)]
#![feature(range_is_empty)]
/// which can be further configured with the `set_span` method below.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
pub fn new(ch: char, spacing: Spacing) -> Punct {
- const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^',
- '&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\''];
- if !LEGAL_CHARS.contains(&ch) {
- panic!("unsupported character `{:?}`", ch)
- }
Punct(bridge::client::Punct::new(ch, spacing))
}
pub struct Ident(bridge::client::Ident);
impl Ident {
- fn is_valid(string: &str) -> bool {
- let mut chars = string.chars();
- if let Some(start) = chars.next() {
- (start == '_' || start.is_xid_start())
- && chars.all(|cont| cont == '_' || cont.is_xid_continue())
- } else {
- false
- }
- }
-
/// Creates a new `Ident` with the given `string` as well as the specified
/// `span`.
/// The `string` argument must be a valid identifier permitted by the
/// tokens, requires a `Span` to be specified at construction.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
pub fn new(string: &str, span: Span) -> Ident {
- if !Ident::is_valid(string) {
- panic!("`{:?}` is not a valid identifier", string)
- }
Ident(bridge::client::Ident::new(string, span.0, false))
}
/// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
#[unstable(feature = "proc_macro_raw_ident", issue = "54723")]
pub fn new_raw(string: &str, span: Span) -> Ident {
- if !Ident::is_valid(string) {
- panic!("`{:?}` is not a valid identifier", string)
- }
Ident(bridge::client::Ident::new(string, span.0, true))
}
args: I) -> CFGIndex {
let func_or_rcvr_exit = self.expr(func_or_rcvr, pred);
let ret = self.straightline(call_expr, func_or_rcvr_exit, args);
- // FIXME(canndrew): This is_never should probably be an is_uninhabited.
- if self.tables.expr_ty(call_expr).is_never() {
+ let m = self.tcx.hir().get_module_parent(call_expr.id);
+ if self.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(call_expr)) {
self.add_unreachable_node()
} else {
ret
}
}
- #[inline]
+ // FIXME: Make `is_anon`, `is_input`, `is_eval_always` and `has_params` properties
+ // of queries
+ #[inline(always)]
pub fn is_anon(&self) -> bool {
match *self {
$(
}
}
- #[inline]
+ #[inline(always)]
pub fn is_input(&self) -> bool {
match *self {
$(
}
}
- #[inline]
+ #[inline(always)]
pub fn is_eval_always(&self) -> bool {
match *self {
$(
}
#[allow(unreachable_code)]
- #[inline]
+ #[inline(always)]
pub fn has_params(&self) -> bool {
match *self {
$(
impl DepNode {
#[allow(unreachable_code, non_snake_case)]
+ #[inline(always)]
pub fn new<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
dep: DepConstructor<'gcx>)
-> DepNode
/// Construct a DepNode from the given DepKind and DefPathHash. This
/// method will assert that the given DepKind actually requires a
/// single DefId/DefPathHash parameter.
- #[inline]
+ #[inline(always)]
pub fn from_def_path_hash(kind: DepKind,
def_path_hash: DefPathHash)
-> DepNode {
- assert!(kind.can_reconstruct_query_key() && kind.has_params());
+ debug_assert!(kind.can_reconstruct_query_key() && kind.has_params());
DepNode {
kind,
hash: def_path_hash.0,
/// Create a new, parameterless DepNode. This method will assert
/// that the DepNode corresponding to the given DepKind actually
/// does not require any parameters.
- #[inline]
+ #[inline(always)]
pub fn new_no_params(kind: DepKind) -> DepNode {
- assert!(!kind.has_params());
+ debug_assert!(!kind.has_params());
DepNode {
kind,
hash: Fingerprint::ZERO,
impl DefPathHash {
- #[inline]
+ #[inline(always)]
pub fn to_dep_node(self, kind: DepKind) -> DepNode {
DepNode::from_def_path_hash(kind, self)
}
}
impl DefId {
- #[inline]
+ #[inline(always)]
pub fn to_dep_node(self, tcx: TyCtxt<'_, '_, '_>, kind: DepKind) -> DepNode {
DepNode::from_def_path_hash(kind, tcx.def_path_hash(self))
}
[] TypeOpNormalizeFnSig(CanonicalTypeOpNormalizeGoal<'tcx, FnSig<'tcx>>),
[] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) },
+ [] MethodAutoderefSteps(CanonicalTyGoal<'tcx>),
[input] TargetFeaturesWhitelist,
self.dep_graph.read(DepNode::new_no_params(DepKind::Krate));
&self.krate
}
+
+ /// This is internally in the depedency tracking system.
+ /// Use the `krate` method to ensure your dependency on the
+ /// crate is tracked.
+ pub fn untracked_krate<'hir>(&'hir self) -> &'hir Crate {
+ &self.krate
+ }
}
/// Represents a mapping from Node IDs to AST elements and their parent
if i > 0 {
self.s.word("::")?
}
- if segment.ident.name != keywords::PathRoot.name() &&
- segment.ident.name != keywords::DollarCrate.name() {
+ if segment.ident.name != keywords::PathRoot.name() {
self.print_ident(segment.ident)?;
segment.with_generic_args(|generic_args| {
self.print_generic_args(generic_args, segment.infer_types,
}
pub fn print_path_segment(&mut self, segment: &hir::PathSegment) -> io::Result<()> {
- if segment.ident.name != keywords::PathRoot.name() &&
- segment.ident.name != keywords::DollarCrate.name() {
+ if segment.ident.name != keywords::PathRoot.name() {
self.print_ident(segment.ident)?;
segment.with_generic_args(|generic_args| {
self.print_generic_args(generic_args, segment.infer_types, false)
if i > 0 {
self.s.word("::")?
}
- if segment.ident.name != keywords::PathRoot.name() &&
- segment.ident.name != keywords::DollarCrate.name() {
+ if segment.ident.name != keywords::PathRoot.name() {
self.print_ident(segment.ident)?;
segment.with_generic_args(|generic_args| {
self.print_generic_args(generic_args,
// The `krate` here is only used for mapping BodyIds to Bodies.
// Don't use it for anything else or you'll run the risk of
// leaking data out of the tracking system.
+ #[inline]
pub fn new(sess: &'a Session,
krate: &'a hir::Crate,
definitions: &'a Definitions,
SetDiscriminant { place, variant_index },
StorageLive(place),
StorageDead(place),
- EscapeToRaw(place),
- Retag { fn_entry, two_phase, place },
+ Retag(retag_kind, place),
AscribeUserType(place, variance, c_ty),
Nop,
InlineAsm { asm, outputs, inputs },
});
+impl_stable_hash_for!(enum mir::RetagKind { FnEntry, TwoPhase, Raw, Default });
impl_stable_hash_for!(enum mir::FakeReadCause { ForMatchGuard, ForMatchedPlace, ForLet });
impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Place<'gcx> {
Ok(Lrc::new(canonical_result))
}
+ /// A version of `make_canonicalized_query_response` that does
+ /// not pack in obligations, for contexts that want to drop
+ /// pending obligations instead of treating them as an ambiguity (e.g.
+ /// typeck "probing" contexts).
+ ///
+ /// If you DO want to keep track of pending obligations (which
+ /// include all region obligations, so this includes all cases
+ /// that care about regions) with this function, you have to
+ /// do it yourself, by e.g. having them be a part of the answer.
+ pub fn make_query_response_ignoring_pending_obligations<T>(
+ &self,
+ inference_vars: CanonicalVarValues<'tcx>,
+ answer: T
+ ) -> Canonical<'gcx, QueryResponse<'gcx, <T as Lift<'gcx>>::Lifted>>
+ where
+ T: Debug + Lift<'gcx> + TypeFoldable<'tcx>,
+ {
+ self.canonicalize_response(&QueryResponse {
+ var_values: inference_vars,
+ region_constraints: vec![],
+ certainty: Certainty::Proven, // Ambiguities are OK!
+ value: answer,
+ })
+ }
+
/// Helper for `make_canonicalized_query_response` that does
/// everything up until the final canonicalization.
fn make_query_response<T>(
use ty::fold::TypeFoldable;
use ty::relate::{RelateResult, TraitObjectMode};
use ty::subst::{Kind, Substs};
-use ty::{self, GenericParamDefKind, Ty, TyCtxt};
+use ty::{self, GenericParamDefKind, Ty, TyCtxt, CtxtInterners};
use ty::{FloatVid, IntVid, TyVid};
use util::nodemap::FxHashMap;
pub struct InferCtxtBuilder<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
global_tcx: TyCtxt<'a, 'gcx, 'gcx>,
arena: SyncDroplessArena,
+ interners: Option<CtxtInterners<'tcx>>,
fresh_tables: Option<RefCell<ty::TypeckTables<'tcx>>>,
trait_object_mode: TraitObjectMode,
}
InferCtxtBuilder {
global_tcx: self,
arena: SyncDroplessArena::default(),
+ interners: None,
fresh_tables: None,
trait_object_mode: TraitObjectMode::NoSquash,
}
global_tcx,
trait_object_mode,
ref arena,
+ ref mut interners,
ref fresh_tables,
} = *self;
let in_progress_tables = fresh_tables.as_ref();
- global_tcx.enter_local(arena, |tcx| {
+ // Check that we haven't entered before
+ assert!(interners.is_none());
+ global_tcx.enter_local(arena, interners, |tcx| {
f(InferCtxt {
tcx,
in_progress_tables,
self.inlined_shallow_resolve(typ)
}
+ pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid {
+ self.type_variables.borrow_mut().root_var(var)
+ }
+
pub fn resolve_type_vars_if_possible<T>(&self, value: &T) -> T
where
T: TypeFoldable<'tcx>,
#![feature(slice_sort_by_cached_key)]
#![feature(specialization)]
#![feature(unboxed_closures)]
+#![feature(thread_local)]
#![feature(trace_macros)]
#![feature(trusted_len)]
#![feature(vec_remove_item)]
#![feature(step_trait)]
+#![feature(stmt_expr_attributes)]
#![feature(integer_atomics)]
#![feature(test)]
#![feature(in_band_lifetimes)]
DerefTraitLangItem, "deref", deref_trait, Target::Trait;
DerefMutTraitLangItem, "deref_mut", deref_mut_trait, Target::Trait;
+ ReceiverTraitLangItem, "receiver", receiver_trait, Target::Trait;
FnTraitLangItem, "fn", fn_trait, Target::Trait;
FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait;
}
hir::ExprKind::Call(ref f, ref args) => {
- // FIXME(canndrew): This is_never should really be an is_uninhabited
- let succ = if self.tables.expr_ty(expr).is_never() {
+ let m = self.ir.tcx.hir().get_module_parent(expr.id);
+ let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
self.s.exit_ln
} else {
succ
}
hir::ExprKind::MethodCall(.., ref args) => {
- // FIXME(canndrew): This is_never should really be an is_uninhabited
- let succ = if self.tables.expr_ty(expr).is_never() {
+ let m = self.ir.tcx.hir().get_module_parent(expr.id);
+ let succ = if self.ir.tcx.is_ty_uninhabited_from(m, self.tables.expr_ty(expr)) {
self.s.exit_ln
} else {
succ
/// by miri and only generated when "-Z mir-emit-retag" is passed.
/// See <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/>
/// for more details.
- Retag {
- /// `fn_entry` indicates whether this is the initial retag that happens in the
- /// function prolog.
- fn_entry: bool,
- /// `two_phase` indicates whether this is just the reservation action of
- /// a two-phase borrow.
- two_phase: bool,
- /// The place to retag
- place: Place<'tcx>,
- },
-
- /// Escape the given reference to a raw pointer, so that it can be accessed
- /// without precise provenance tracking. These statements are currently only interpreted
- /// by miri and only generated when "-Z mir-emit-retag" is passed.
- /// See <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/>
- /// for more details.
- EscapeToRaw(Operand<'tcx>),
+ Retag(RetagKind, Place<'tcx>),
/// Encodes a user's type ascription. These need to be preserved
/// intact so that NLL can respect them. For example:
Nop,
}
+/// `RetagKind` describes what kind of retag is to be performed.
+#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq)]
+pub enum RetagKind {
+ /// The initial retag when entering a function
+ FnEntry,
+ /// Retag preparing for a two-phase borrow
+ TwoPhase,
+ /// Retagging raw pointers
+ Raw,
+ /// A "normal" retag
+ Default,
+}
+
/// The `FakeReadCause` describes the type of pattern why a `FakeRead` statement exists.
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum FakeReadCause {
match self.kind {
Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv),
FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
- Retag { fn_entry, two_phase, ref place } =>
- write!(fmt, "Retag({}{}{:?})",
- if fn_entry { "[fn entry] " } else { "" },
- if two_phase { "[2phase] " } else { "" },
+ Retag(ref kind, ref place) =>
+ write!(fmt, "Retag({}{:?})",
+ match kind {
+ RetagKind::FnEntry => "[fn entry] ",
+ RetagKind::TwoPhase => "[2phase] ",
+ RetagKind::Raw => "[raw] ",
+ RetagKind::Default => "",
+ },
place,
),
- EscapeToRaw(ref place) => write!(fmt, "EscapeToRaw({:?})", place),
StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place),
StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place),
SetDiscriminant {
SourceInfo,
UpvarDecl,
FakeReadCause,
+ RetagKind,
SourceScope,
SourceScopeData,
SourceScopeLocalData,
(StatementKind::StorageLive)(a),
(StatementKind::StorageDead)(a),
(StatementKind::InlineAsm) { asm, outputs, inputs },
- (StatementKind::Retag) { fn_entry, two_phase, place },
- (StatementKind::EscapeToRaw)(place),
+ (StatementKind::Retag)(kind, place),
(StatementKind::AscribeUserType)(a, v, b),
(StatementKind::Nop),
}
}
fn visit_retag(&mut self,
- fn_entry: & $($mutability)* bool,
- two_phase: & $($mutability)* bool,
+ kind: & $($mutability)* RetagKind,
place: & $($mutability)* Place<'tcx>,
location: Location) {
- self.super_retag(fn_entry, two_phase, place, location);
+ self.super_retag(kind, place, location);
}
fn visit_place(&mut self,
location
);
}
- StatementKind::EscapeToRaw(ref $($mutability)* op) => {
- self.visit_operand(op, location);
- }
StatementKind::StorageLive(ref $($mutability)* local) => {
self.visit_local(
local,
self.visit_operand(input, location);
}
}
- StatementKind::Retag { ref $($mutability)* fn_entry,
- ref $($mutability)* two_phase,
- ref $($mutability)* place } => {
- self.visit_retag(fn_entry, two_phase, place, location);
+ StatementKind::Retag ( ref $($mutability)* kind,
+ ref $($mutability)* place ) => {
+ self.visit_retag(kind, place, location);
}
StatementKind::AscribeUserType(
ref $($mutability)* place,
}
fn super_retag(&mut self,
- _fn_entry: & $($mutability)* bool,
- _two_phase: & $($mutability)* bool,
+ _kind: & $($mutability)* RetagKind,
place: & $($mutability)* Place<'tcx>,
location: Location) {
self.visit_place(
/// Used by -Z profile-queries in util::common
pub profile_channel: Lock<Option<mpsc::Sender<ProfileQueriesMsg>>>,
+ /// Used by -Z self-profile
+ pub self_profiling_active: bool,
+
/// Used by -Z self-profile
pub self_profiling: Lock<SelfProfiler>,
}
}
+ #[inline(never)]
+ #[cold]
+ fn profiler_active<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
+ let mut profiler = self.self_profiling.borrow_mut();
+ f(&mut profiler);
+ }
+
+ #[inline(always)]
pub fn profiler<F: FnOnce(&mut SelfProfiler) -> ()>(&self, f: F) {
- if self.opts.debugging_opts.self_profile || self.opts.debugging_opts.profile_json {
- let mut profiler = self.self_profiling.borrow_mut();
- f(&mut profiler);
+ if unlikely!(self.self_profiling_active) {
+ self.profiler_active(f)
}
}
CguReuseTracker::new_disabled()
};
+ let self_profiling_active = sopts.debugging_opts.self_profile ||
+ sopts.debugging_opts.profile_json;
+
let sess = Session {
target: target_cfg,
host,
imported_macro_spans: OneThread::new(RefCell::new(FxHashMap::default())),
incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)),
cgu_reuse_tracker,
+ self_profiling_active,
self_profiling: Lock::new(SelfProfiler::new()),
profile_channel: Lock::new(None),
perf_stats: PerfStats {
-> Vec<Ty<'tcx>> {
if ty_is_local_constructor(ty, in_crate) {
vec![]
- } else if fundamental_ty(tcx, ty) {
+ } else if fundamental_ty(ty) {
ty.walk_shallow()
.flat_map(|t| uncovered_tys(tcx, t, in_crate))
.collect()
fn ty_is_local(tcx: TyCtxt<'_, '_, '_>, ty: Ty<'_>, in_crate: InCrate) -> bool {
ty_is_local_constructor(ty, in_crate) ||
- fundamental_ty(tcx, ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, in_crate))
+ fundamental_ty(ty) && ty.walk_shallow().any(|t| ty_is_local(tcx, t, in_crate))
}
-fn fundamental_ty(tcx: TyCtxt<'_, '_, '_>, ty: Ty<'_>) -> bool {
+fn fundamental_ty(ty: Ty<'_>) -> bool {
match ty.sty {
ty::Ref(..) => true,
ty::Adt(def, _) => def.is_fundamental(),
- ty::Dynamic(ref data, ..) => tcx.has_attr(data.principal().def_id(), "fundamental"),
_ => false
}
}
// type-lives-for-region constraints, and because the type
// is well-formed, the constraints should hold.
register_region_obligations: bool,
+ // Is it OK to register obligations into this infcx inside
+ // an infcx snapshot?
+ //
+ // The "primary fulfillment" in many cases in typeck lives
+ // outside of any snapshot, so any use of it inside a snapshot
+ // will lead to trouble and therefore is checked against, but
+ // other fulfillment contexts sometimes do live inside of
+ // a snapshot (they don't *straddle* a snapshot, so there
+ // is no trouble there).
+ usable_in_snapshot: bool
}
#[derive(Clone, Debug)]
pub fn new() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
- register_region_obligations: true
+ register_region_obligations: true,
+ usable_in_snapshot: false,
+ }
+ }
+
+ pub fn new_in_snapshot() -> FulfillmentContext<'tcx> {
+ FulfillmentContext {
+ predicates: ObligationForest::new(),
+ register_region_obligations: true,
+ usable_in_snapshot: true,
}
}
pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
- register_region_obligations: false
+ register_region_obligations: false,
+ usable_in_snapshot: false
}
}
debug!("register_predicate_obligation(obligation={:?})", obligation);
- assert!(!infcx.is_in_snapshot());
+ assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
self.predicates.register_obligation(PendingPredicateObligation {
obligation,
--- /dev/null
+// 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 <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.
+
+use rustc_data_structures::sync::Lrc;
+use infer::canonical::{Canonical, QueryResponse};
+use ty::Ty;
+
+#[derive(Debug)]
+pub struct CandidateStep<'tcx> {
+ pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+ pub autoderefs: usize,
+ // true if the type results from a dereference of a raw pointer.
+ // when assembling candidates, we include these steps, but not when
+ // picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods
+ // `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then
+ // `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
+ pub from_unsafe_deref: bool,
+ pub unsize: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct MethodAutoderefStepsResult<'tcx> {
+ /// The valid autoderef steps that could be find.
+ pub steps: Lrc<Vec<CandidateStep<'tcx>>>,
+ /// If Some(T), a type autoderef reported an error on.
+ pub opt_bad_ty: Option<Lrc<MethodAutoderefBadTy<'tcx>>>,
+ /// If `true`, `steps` has been truncated due to reaching the
+ /// recursion limit.
+ pub reached_recursion_limit: bool,
+}
+
+#[derive(Debug)]
+pub struct MethodAutoderefBadTy<'tcx> {
+ pub reached_raw_pointer: bool,
+ pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+}
+
+impl_stable_hash_for!(struct MethodAutoderefBadTy<'tcx> {
+ reached_raw_pointer, ty
+});
+
+impl_stable_hash_for!(struct MethodAutoderefStepsResult<'tcx> {
+ reached_recursion_limit, steps, opt_bad_ty
+});
+
+impl_stable_hash_for!(struct CandidateStep<'tcx> {
+ self_ty, autoderefs, from_unsafe_deref, unsize
+});
pub mod dropck_outlives;
pub mod evaluate_obligation;
+pub mod method_autoderef;
pub mod normalize;
pub mod normalize_erasing_regions;
pub mod outlives_bounds;
use std::iter;
use std::sync::mpsc;
use std::sync::Arc;
+use std::marker::PhantomData;
use rustc_target::spec::abi;
use syntax::ast::{self, NodeId};
use syntax::attr;
pub struct AllArenas<'tcx> {
pub global: WorkerLocal<GlobalArenas<'tcx>>,
pub interner: SyncDroplessArena,
+ global_ctxt: Option<GlobalCtxt<'tcx>>,
}
impl<'tcx> AllArenas<'tcx> {
AllArenas {
global: WorkerLocal::new(|_| GlobalArenas::default()),
interner: SyncDroplessArena::default(),
+ global_ctxt: None,
}
}
}
/// [rustc guide]: https://rust-lang.github.io/rustc-guide/ty.html
#[derive(Copy, Clone)]
pub struct TyCtxt<'a, 'gcx: 'tcx, 'tcx: 'a> {
- gcx: &'a GlobalCtxt<'gcx>,
- interners: &'a CtxtInterners<'tcx>
+ gcx: &'gcx GlobalCtxt<'gcx>,
+ interners: &'tcx CtxtInterners<'tcx>,
+ dummy: PhantomData<&'a ()>,
}
-impl<'a, 'gcx, 'tcx> Deref for TyCtxt<'a, 'gcx, 'tcx> {
- type Target = &'a GlobalCtxt<'gcx>;
+impl<'gcx> Deref for TyCtxt<'_, 'gcx, '_> {
+ type Target = &'gcx GlobalCtxt<'gcx>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.gcx
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
/// Get the global TyCtxt.
#[inline]
- pub fn global_tcx(self) -> TyCtxt<'a, 'gcx, 'gcx> {
+ pub fn global_tcx(self) -> TyCtxt<'gcx, 'gcx, 'gcx> {
TyCtxt {
gcx: self.gcx,
interners: &self.gcx.global_interners,
+ dummy: PhantomData,
}
}
cstore: &'tcx CrateStoreDyn,
local_providers: ty::query::Providers<'tcx>,
extern_providers: ty::query::Providers<'tcx>,
- arenas: &'tcx AllArenas<'tcx>,
+ arenas: &'tcx mut AllArenas<'tcx>,
resolutions: ty::Resolutions,
hir: hir_map::Map<'tcx>,
on_disk_query_result_cache: query::OnDiskCache<'tcx>,
Lrc::new(StableVec::new(v)));
}
- let gcx = &GlobalCtxt {
+ arenas.global_ctxt = Some(GlobalCtxt {
sess: s,
cstore,
global_arenas: &arenas.global,
alloc_map: Lock::new(interpret::AllocMap::new()),
tx_to_llvm_workers: Lock::new(tx),
output_filenames: Arc::new(output_filenames.clone()),
- };
+ });
+
+ let gcx = arenas.global_ctxt.as_ref().unwrap();
sync::assert_send_val(&gcx);
self.cstore.crate_data_as_rc_any(cnum)
}
+ #[inline(always)]
pub fn create_stable_hashing_context(self) -> StableHashingContext<'a> {
- let krate = self.dep_graph.with_ignore(|| self.hir().krate());
+ let krate = self.gcx.hir_map.forest.untracked_krate();
StableHashingContext::new(self.sess,
krate,
}
}
-impl<'gcx: 'tcx, 'tcx> GlobalCtxt<'gcx> {
+impl<'gcx> GlobalCtxt<'gcx> {
/// Call the closure with a local `TyCtxt` using the given arena.
- pub fn enter_local<F, R>(
- &self,
+ /// `interners` is a slot passed so we can create a CtxtInterners
+ /// with the same lifetime as `arena`.
+ pub fn enter_local<'tcx, F, R>(
+ &'gcx self,
arena: &'tcx SyncDroplessArena,
+ interners: &'tcx mut Option<CtxtInterners<'tcx>>,
f: F
) -> R
where
- F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> R
+ F: FnOnce(TyCtxt<'tcx, 'gcx, 'tcx>) -> R,
+ 'gcx: 'tcx,
{
- let interners = CtxtInterners::new(arena);
+ *interners = Some(CtxtInterners::new(&arena));
let tcx = TyCtxt {
gcx: self,
- interners: &interners,
+ interners: interners.as_ref().unwrap(),
+ dummy: PhantomData,
};
ty::tls::with_related_context(tcx.global_tcx(), |icx| {
let new_icx = ty::tls::ImplicitCtxt {
layout_depth: icx.layout_depth,
task: icx.task,
};
- ty::tls::enter_context(&new_icx, |new_icx| {
- f(new_icx.tcx)
+ ty::tls::enter_context(&new_icx, |_| {
+ f(tcx)
})
})
}
use std::fmt;
use std::mem;
+ use std::marker::PhantomData;
use syntax_pos;
use ty::query;
use errors::{Diagnostic, TRACK_DIAGNOSTICS};
/// you should also have access to an ImplicitCtxt through the functions
/// in this module.
#[derive(Clone)]
- pub struct ImplicitCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+ pub struct ImplicitCtxt<'a, 'gcx: 'tcx, 'tcx> {
/// The current TyCtxt. Initially created by `enter_global` and updated
/// by `enter_local` with a new local interner
- pub tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ pub tcx: TyCtxt<'tcx, 'gcx, 'tcx>,
/// The current query job, if any. This is updated by start_job in
/// ty::query::plumbing when executing a query
/// creating a initial TyCtxt and ImplicitCtxt.
/// This happens once per rustc session and TyCtxts only exists
/// inside the `f` function.
- pub fn enter_global<'gcx, F, R>(gcx: &GlobalCtxt<'gcx>, f: F) -> R
- where F: for<'a> FnOnce(TyCtxt<'a, 'gcx, 'gcx>) -> R
+ pub fn enter_global<'gcx, F, R>(gcx: &'gcx GlobalCtxt<'gcx>, f: F) -> R
+ where F: FnOnce(TyCtxt<'gcx, 'gcx, 'gcx>) -> R
{
with_thread_locals(|| {
// Update GCX_PTR to indicate there's a GlobalCtxt available
let tcx = TyCtxt {
gcx,
interners: &gcx.global_interners,
+ dummy: PhantomData,
};
let icx = ImplicitCtxt {
tcx,
let tcx = TyCtxt {
gcx,
interners: &gcx.global_interners,
+ dummy: PhantomData,
};
let icx = ImplicitCtxt {
query: None,
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use util::nodemap::{FxHashMap, FxHashSet};
use ty::context::TyCtxt;
use ty::{AdtDef, VariantDef, FieldDef, Ty, TyS};
use ty::{DefId, Substs};
}
fn ty_inhabitedness_forest(self, ty: Ty<'tcx>) -> DefIdForest {
- ty.uninhabited_from(&mut FxHashMap::default(), self)
+ ty.uninhabited_from(self)
}
pub fn is_enum_variant_uninhabited_from(self,
let adt_kind = self.adt_def(adt_def_id).adt_kind();
// Compute inhabitedness forest:
- variant.uninhabited_from(&mut FxHashMap::default(), self, substs, adt_kind)
+ variant.uninhabited_from(self, substs, adt_kind)
}
}
/// Calculate the forest of DefIds from which this adt is visibly uninhabited.
fn uninhabited_from(
&self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>) -> DefIdForest
{
DefIdForest::intersection(tcx, self.variants.iter().map(|v| {
- v.uninhabited_from(visited, tcx, substs, self.adt_kind())
+ v.uninhabited_from(tcx, substs, self.adt_kind())
}))
}
}
/// Calculate the forest of DefIds from which this variant is visibly uninhabited.
fn uninhabited_from(
&self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>,
adt_kind: AdtKind) -> DefIdForest
AdtKind::Struct => false,
};
DefIdForest::union(tcx, self.fields.iter().map(|f| {
- f.uninhabited_from(visited, tcx, substs, is_enum)
+ f.uninhabited_from(tcx, substs, is_enum)
}))
}
}
/// Calculate the forest of DefIds from which this field is visibly uninhabited.
fn uninhabited_from(
&self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
substs: &'tcx Substs<'tcx>,
is_enum: bool,
) -> DefIdForest {
- let mut data_uninhabitedness = move || {
- self.ty(tcx, substs).uninhabited_from(visited, tcx)
+ let data_uninhabitedness = move || {
+ self.ty(tcx, substs).uninhabited_from(tcx)
};
// FIXME(canndrew): Currently enum fields are (incorrectly) stored with
// Visibility::Invisible so we need to override self.vis if we're
impl<'a, 'gcx, 'tcx> TyS<'tcx> {
/// Calculate the forest of DefIds from which this type is visibly uninhabited.
- fn uninhabited_from(
- &self,
- visited: &mut FxHashMap<DefId, FxHashSet<&'tcx Substs<'tcx>>>,
- tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
+ fn uninhabited_from(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> DefIdForest
{
match self.sty {
- Adt(def, substs) => {
- {
- let substs_set = visited.entry(def.did).or_default();
- if !substs_set.insert(substs) {
- // We are already calculating the inhabitedness of this type.
- // The type must contain a reference to itself. Break the
- // infinite loop.
- return DefIdForest::empty();
- }
- if substs_set.len() >= tcx.sess.recursion_limit.get() / 4 {
- // We have gone very deep, reinstantiating this ADT inside
- // itself with different type arguments. We are probably
- // hitting an infinite loop. For example, it's possible to write:
- // a type Foo<T>
- // which contains a Foo<(T, T)>
- // which contains a Foo<((T, T), (T, T))>
- // which contains a Foo<(((T, T), (T, T)), ((T, T), (T, T)))>
- // etc.
- let error = format!("reached recursion limit while checking \
- inhabitedness of `{}`", self);
- tcx.sess.fatal(&error);
- }
- }
- let ret = def.uninhabited_from(visited, tcx, substs);
- let substs_set = visited.get_mut(&def.did).unwrap();
- substs_set.remove(substs);
- ret
- }
+ Adt(def, substs) => def.uninhabited_from(tcx, substs),
Never => DefIdForest::full(tcx),
Tuple(ref tys) => {
DefIdForest::union(tcx, tys.iter().map(|ty| {
- ty.uninhabited_from(visited, tcx)
+ ty.uninhabited_from(tcx)
}))
}
match len.assert_usize(tcx) {
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
- Some(n) if n != 0 => ty.uninhabited_from(visited, tcx),
+ Some(n) if n != 0 => ty.uninhabited_from(tcx),
_ => DefIdForest::empty()
}
}
ty::tls::enter_context(&icx, |_| {
let cx = LayoutCx { tcx, param_env };
- cx.layout_raw_uncached(ty)
+ let layout = cx.layout_raw_uncached(ty);
+ // Type-level uninhabitedness should always imply ABI uninhabitedness.
+ if let Ok(layout) = layout {
+ if ty.conservative_is_privately_uninhabited(tcx) {
+ assert!(layout.abi.is_uninhabited());
+ }
+ }
+ layout
})
})
}
pub struct LayoutCx<'tcx, C> {
pub tcx: C,
- pub param_env: ty::ParamEnv<'tcx>
+ pub param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> {
- fn layout_raw_uncached(&self, ty: Ty<'tcx>)
- -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> {
+ fn layout_raw_uncached(&self, ty: Ty<'tcx>) -> Result<&'tcx LayoutDetails, LayoutError<'tcx>> {
let tcx = self.tcx;
let param_env = self.param_env;
let dl = self.data_layout();
let size = element.size.checked_mul(count, dl)
.ok_or(LayoutError::SizeOverflow(ty))?;
+ let abi = if count != 0 && ty.conservative_is_privately_uninhabited(tcx) {
+ Abi::Uninhabited
+ } else {
+ Abi::Aggregate { sized: true }
+ };
+
tcx.intern_layout(LayoutDetails {
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldPlacement::Array {
stride: element.size,
count
},
- abi: Abi::Aggregate { sized: true },
+ abi,
align: element.align,
size
})
pub use self::binding::BindingMode::*;
pub use self::context::{TyCtxt, FreeRegionInfo, GlobalArenas, AllArenas, tls, keep_local};
-pub use self::context::{Lift, TypeckTables};
+pub use self::context::{Lift, TypeckTables, CtxtInterners};
pub use self::instance::{Instance, InstanceDef};
}
}
+impl<'tcx> QueryDescription<'tcx> for queries::method_autoderef_steps<'tcx> {
+ fn describe(_tcx: TyCtxt<'_, '_, '_>, goal: CanonicalTyGoal<'tcx>) -> Cow<'static, str> {
+ format!("computing autoderef types for `{:?}`", goal).into()
+ }
+}
+
impl<'tcx> QueryDescription<'tcx> for queries::target_features_whitelist<'tcx> {
fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> {
"looking up the whitelist of target features".into()
use ty::tls;
use ty::query::Query;
use ty::query::plumbing::CycleError;
+#[cfg(not(parallel_queries))]
+use ty::query::{
+ plumbing::TryGetJob,
+ config::QueryDescription,
+};
use ty::context::TyCtxt;
use errors::Diagnostic;
use std::process;
///
/// For single threaded rustc there's no concurrent jobs running, so if we are waiting for any
/// query that means that there is a query cycle, thus this always running a cycle error.
- pub(super) fn await<'lcx>(
+ #[cfg(not(parallel_queries))]
+ #[inline(never)]
+ #[cold]
+ pub(super) fn cycle_error<'lcx, 'a, D: QueryDescription<'tcx>>(
&self,
tcx: TyCtxt<'_, 'tcx, 'lcx>,
span: Span,
- ) -> Result<(), CycleError<'tcx>> {
- #[cfg(not(parallel_queries))]
- {
- self.find_cycle_in_stack(tcx, span)
- }
+ ) -> TryGetJob<'a, 'tcx, D> {
+ TryGetJob::JobCompleted(Err(Box::new(self.find_cycle_in_stack(tcx, span))))
+ }
- #[cfg(parallel_queries)]
- {
- tls::with_related_context(tcx, move |icx| {
- let mut waiter = Lrc::new(QueryWaiter {
- query: icx.query.clone(),
- span,
- cycle: Lock::new(None),
- condvar: Condvar::new(),
- });
- self.latch.await(&waiter);
- // FIXME: Get rid of this lock. We have ownership of the QueryWaiter
- // although another thread may still have a Lrc reference so we cannot
- // use Lrc::get_mut
- let mut cycle = waiter.cycle.lock();
- match cycle.take() {
- None => Ok(()),
- Some(cycle) => Err(cycle)
- }
- })
- }
+ /// Awaits for the query job to complete.
+ ///
+ /// For single threaded rustc there's no concurrent jobs running, so if we are waiting for any
+ /// query that means that there is a query cycle, thus this always running a cycle error.
+ #[cfg(parallel_queries)]
+ pub(super) fn await<'lcx>(
+ &self,
+ tcx: TyCtxt<'_, 'tcx, 'lcx>,
+ span: Span,
+ ) -> Result<(), Box<CycleError<'tcx>>> {
+ tls::with_related_context(tcx, move |icx| {
+ let mut waiter = Lrc::new(QueryWaiter {
+ query: icx.query.clone(),
+ span,
+ cycle: Lock::new(None),
+ condvar: Condvar::new(),
+ });
+ self.latch.await(&waiter);
+ // FIXME: Get rid of this lock. We have ownership of the QueryWaiter
+ // although another thread may still have a Lrc reference so we cannot
+ // use Lrc::get_mut
+ let mut cycle = waiter.cycle.lock();
+ match cycle.take() {
+ None => Ok(()),
+ Some(cycle) => Err(Box::new(cycle))
+ }
+ })
}
#[cfg(not(parallel_queries))]
&self,
tcx: TyCtxt<'_, 'tcx, 'lcx>,
span: Span,
- ) -> Result<(), CycleError<'tcx>> {
+ ) -> CycleError<'tcx> {
// Get the current executing query (waiter) and find the waitee amongst its parents
let mut current_job = tls::with_related_context(tcx, |icx| icx.query.clone());
let mut cycle = Vec::new();
let usage = job.parent.as_ref().map(|parent| {
(job.info.span, parent.info.query.clone())
});
- return Err(CycleError { usage, cycle });
+ return CycleError { usage, cycle };
}
current_job = job.parent.clone();
CanonicalTypeOpSubtypeGoal, CanonicalTypeOpProvePredicateGoal,
CanonicalTypeOpNormalizeGoal, NoSolution,
};
+use traits::query::method_autoderef::MethodAutoderefStepsResult;
use traits::query::dropck_outlives::{DtorckConstraint, DropckOutlivesResult};
use traits::query::normalize::NormalizationResult;
use traits::query::outlives_bounds::OutlivesBound;
[] fn substitute_normalize_and_test_predicates:
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
+
+ [] fn method_autoderef_steps: MethodAutoderefSteps(
+ CanonicalTyGoal<'tcx>
+ ) -> MethodAutoderefStepsResult<'tcx>,
},
Other {
self,
span: Span,
key: DefId,
- ) -> Result<&'tcx [Ty<'tcx>], DiagnosticBuilder<'a>> {
+ ) -> Result<&'tcx [Ty<'tcx>], Box<DiagnosticBuilder<'a>>> {
self.try_get_query::<queries::adt_sized_constraint<'_>>(span, key)
}
pub fn try_needs_drop_raw(
self,
span: Span,
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
- ) -> Result<bool, DiagnosticBuilder<'a>> {
+ ) -> Result<bool, Box<DiagnosticBuilder<'a>>> {
self.try_get_query::<queries::needs_drop_raw<'_>>(span, key)
}
pub fn try_optimized_mir(
self,
span: Span,
key: DefId,
- ) -> Result<&'tcx mir::Mir<'tcx>, DiagnosticBuilder<'a>> {
+ ) -> Result<&'tcx mir::Mir<'tcx>, Box<DiagnosticBuilder<'a>>> {
self.try_get_query::<queries::optimized_mir<'_>>(span, key)
}
}
};
mem::drop(lock);
- if let Err(cycle) = job.await(tcx, span) {
- return TryGetJob::JobCompleted(Err(cycle));
+ // If we are single-threaded we know that we have cycle error,
+ // so we just turn the errror
+ #[cfg(not(parallel_queries))]
+ return job.cycle_error(tcx, span);
+
+ // With parallel queries we might just have to wait on some other
+ // thread
+ #[cfg(parallel_queries)]
+ {
+ if let Err(cycle) = job.await(tcx, span) {
+ return TryGetJob::JobCompleted(Err(cycle));
+ }
}
}
}
let r = tls::with_related_context(tcx, move |current_icx| {
// Update the ImplicitCtxt to point to our new query job
let new_icx = tls::ImplicitCtxt {
- tcx,
+ tcx: tcx.global_tcx(),
query: Some(self.job.clone()),
layout_depth: current_icx.layout_depth,
task: current_icx.task,
/// The query was already completed.
/// Returns the result of the query and its dep node index
/// if it succeeded or a cycle error if it failed
- JobCompleted(Result<(D::Value, DepNodeIndex), CycleError<'tcx>>),
+ JobCompleted(Result<(D::Value, DepNodeIndex), Box<CycleError<'tcx>>>),
}
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
- pub(super) fn report_cycle(self, CycleError { usage, cycle: stack }: CycleError<'gcx>)
- -> DiagnosticBuilder<'a>
+ #[inline(never)]
+ #[cold]
+ pub(super) fn report_cycle(
+ self,
+ box CycleError { usage, cycle: stack }: Box<CycleError<'gcx>>
+ ) -> Box<DiagnosticBuilder<'a>>
{
assert!(!stack.is_empty());
&format!("cycle used when {}", query.describe(self)));
}
- return err
+ return Box::new(err)
})
}
}
}
+ #[inline(never)]
fn try_get_with<Q: QueryDescription<'gcx>>(
self,
span: Span,
key: Q::Key)
- -> Result<Q::Value, CycleError<'gcx>>
+ -> Result<Q::Value, Box<CycleError<'gcx>>>
{
debug!("ty::queries::{}::try_get_with(key={:?}, span={:?})",
Q::NAME,
job: JobOwner<'a, 'gcx, Q>,
dep_node_index: DepNodeIndex,
dep_node: &DepNode
- ) -> Result<Q::Value, CycleError<'gcx>>
+ ) -> Result<Q::Value, Box<CycleError<'gcx>>>
{
// Note this function can be called concurrently from the same query
// We must ensure that this is handled correctly
key: Q::Key,
job: JobOwner<'_, 'gcx, Q>,
dep_node: DepNode)
- -> Result<(Q::Value, DepNodeIndex), CycleError<'gcx>> {
+ -> Result<(Q::Value, DepNodeIndex), Box<CycleError<'gcx>>> {
// If the following assertion triggers, it can have two reasons:
// 1. Something is wrong with DepNode creation, either here or
// in DepGraph::try_mark_green()
key: Q::Key,
span: Span,
dep_node: DepNode
- ) -> Result<(Q::Value, DepNodeIndex), CycleError<'gcx>> {
+ ) {
+ profq_msg!(
+ self,
+ ProfileQueriesMsg::QueryBegin(span.data(), profq_query_msg!(Q::NAME, self, key))
+ );
+
// We may be concurrently trying both execute and force a query
// Ensure that only one of them runs the query
let job = match JobOwner::try_get(self, span, &key) {
TryGetJob::NotYetStarted(job) => job,
- TryGetJob::JobCompleted(result) => return result,
+ TryGetJob::JobCompleted(_) => return,
};
- self.force_query_with_job::<Q>(key, job, dep_node)
+ if let Err(e) = self.force_query_with_job::<Q>(key, job, dep_node) {
+ self.report_cycle(e).emit();
+ }
}
pub(super) fn try_get_query<Q: QueryDescription<'gcx>>(
self,
span: Span,
key: Q::Key,
- ) -> Result<Q::Value, DiagnosticBuilder<'a>> {
+ ) -> Result<Q::Value, Box<DiagnosticBuilder<'a>>> {
match self.try_get_with::<Q>(span, key) {
Ok(e) => Ok(e),
Err(e) => Err(self.report_cycle(e)),
}
}
+ // FIXME: Try uninlining this
+ #[inline(always)]
pub(super) fn get_query<Q: QueryDescription<'gcx>>(
self,
span: Span,
key: Q::Key,
) -> Q::Value {
- self.try_get_query::<Q>(span, key).unwrap_or_else(|mut e| {
- e.emit();
- Q::handle_cycle_error(self)
+ self.try_get_with::<Q>(span, key).unwrap_or_else(|e| {
+ self.emit_error::<Q>(e)
})
}
+
+ #[inline(never)]
+ #[cold]
+ fn emit_error<Q: QueryDescription<'gcx>>(
+ self,
+ e: Box<CycleError<'gcx>>,
+ ) -> Q::Value {
+ self.report_cycle(e).emit();
+ Q::handle_cycle_error(self)
+ }
}
macro_rules! handle_cycle_error {
}
impl<$tcx> QueryAccessors<$tcx> for queries::$name<$tcx> {
+ #[inline(always)]
fn query(key: Self::Key) -> Query<'tcx> {
Query::$name(key)
}
+ #[inline(always)]
fn query_cache<'a>(tcx: TyCtxt<'a, $tcx, '_>) -> &'a Lock<QueryCache<$tcx, Self>> {
&tcx.queries.$name
}
#[allow(unused)]
+ #[inline(always)]
fn to_dep_node(tcx: TyCtxt<'_, $tcx, '_>, key: &Self::Key) -> DepNode {
use dep_graph::DepConstructor::*;
impl<'a, 'gcx, 'tcx> Deref for TyCtxtAt<'a, 'gcx, 'tcx> {
type Target = TyCtxt<'a, 'gcx, 'tcx>;
+ #[inline(always)]
fn deref(&self) -> &Self::Target {
&self.tcx
}
impl<'a, $tcx, 'lcx> TyCtxt<'a, $tcx, 'lcx> {
/// Return a transparent wrapper for `TyCtxt` which uses
/// `span` as the location of queries performed through it.
+ #[inline(always)]
pub fn at(self, span: Span) -> TyCtxtAt<'a, $tcx, 'lcx> {
TyCtxtAt {
tcx: self,
}
$($(#[$attr])*
+ #[inline(always)]
pub fn $name(self, key: $K) -> $V {
self.at(DUMMY_SP).$name(key)
})*
impl<'a, $tcx, 'lcx> TyCtxtAt<'a, $tcx, 'lcx> {
$($(#[$attr])*
+ #[inline(always)]
pub fn $name(self, key: $K) -> $V {
self.tcx.get_query::<queries::$name<'_>>(self.span, key)
})*
macro_rules! force {
($query:ident, $key:expr) => {
{
- use $crate::util::common::{ProfileQueriesMsg, profq_msg};
-
- profq_msg!(tcx,
- ProfileQueriesMsg::QueryBegin(
- DUMMY_SP.data(),
- profq_query_msg!(::ty::query::queries::$query::NAME, tcx, $key),
- )
- );
-
- if let Err(e) = tcx.force_query::<::ty::query::queries::$query<'_>>(
- $key, DUMMY_SP, *dep_node
- ) {
- tcx.report_cycle(e).emit();
- }
+ tcx.force_query::<::ty::query::queries::$query<'_>>($key, DUMMY_SP, *dep_node);
}
}
};
DepKind::TypeOpNormalizePolyFnSig |
DepKind::TypeOpNormalizeFnSig |
DepKind::SubstituteNormalizeAndTestPredicates |
+ DepKind::MethodAutoderefSteps |
DepKind::InstanceDefSizeEstimate |
DepKind::ProgramClausesForEnv |
}
}
+ /// Checks whether a type is definitely uninhabited. This is
+ /// conservative: for some types that are uninhabited we return `false`,
+ /// but we only return `true` for types that are definitely uninhabited.
+ /// `ty.conservative_is_privately_uninhabited` implies that any value of type `ty`
+ /// will be `Abi::Uninhabited`. (Note that uninhabited types may have nonzero
+ /// size, to account for partial initialisation. See #49298 for details.)
+ pub fn conservative_is_privately_uninhabited(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
+ // FIXME(varkor): we can make this less conversative by substituting concrete
+ // type arguments.
+ match self.sty {
+ ty::Never => true,
+ ty::Adt(def, _) if def.is_union() => {
+ // For now, `union`s are never considered uninhabited.
+ false
+ }
+ ty::Adt(def, _) => {
+ // Any ADT is uninhabited if either:
+ // (a) It has no variants (i.e. an empty `enum`);
+ // (b) Each of its variants (a single one in the case of a `struct`) has at least
+ // one uninhabited field.
+ def.variants.iter().all(|var| {
+ var.fields.iter().any(|field| {
+ tcx.type_of(field.did).conservative_is_privately_uninhabited(tcx)
+ })
+ })
+ }
+ ty::Tuple(tys) => tys.iter().any(|ty| ty.conservative_is_privately_uninhabited(tcx)),
+ ty::Array(ty, len) => {
+ match len.assert_usize(tcx) {
+ // If the array is definitely non-empty, it's uninhabited if
+ // the type of its elements is uninhabited.
+ Some(n) if n != 0 => ty.conservative_is_privately_uninhabited(tcx),
+ _ => false
+ }
+ }
+ ty::Ref(..) => {
+ // References to uninitialised memory is valid for any type, including
+ // uninhabited types, in unsafe code, so we treat all references as
+ // inhabited.
+ false
+ }
+ _ => false,
+ }
+ }
+
pub fn is_primitive(&self) -> bool {
match self.sty {
Bool | Char | Int(_) | Uint(_) | Float(_) => true,
}
mir::StatementKind::FakeRead(..) |
mir::StatementKind::Retag { .. } |
- mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => bx,
}
}
impl stable_hasher::StableHasherResult for Fingerprint {
+ #[inline]
fn finish(hasher: stable_hasher::StableHasher<Self>) -> Self {
let (_0, _1) = hasher.finalize();
Fingerprint(_0, _1)
#![feature(allow_internal_unstable)]
#![feature(vec_resize_with)]
#![feature(hash_raw_entry)]
+#![feature(stmt_expr_attributes)]
+#![feature(core_intrinsics)]
#![cfg_attr(unix, feature(libc))]
#![cfg_attr(test, feature(test))]
pub use rustc_serialize::hex::ToHex;
+#[macro_export]
+macro_rules! likely {
+ ($e:expr) => {
+ #[allow(unused_unsafe)]
+ {
+ unsafe { std::intrinsics::likely($e) }
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! unlikely {
+ ($e:expr) => {
+ #[allow(unused_unsafe)]
+ {
+ unsafe { std::intrinsics::unlikely($e) }
+ }
+ }
+}
+
pub mod macros;
pub mod svh;
pub mod base_n;
}
}
- let arenas = AllArenas::new();
-
// Construct the HIR map
let hir_map = time(sess, "indexing hir", || {
hir_map::map_crate(sess, cstore, &mut hir_forest, &defs)
sess,
outdir,
output,
- &arenas,
&cstore,
&hir_map,
&analysis,
None
};
+ let mut arenas = AllArenas::new();
+
phase_3_run_analysis_passes(
&*codegen_backend,
control,
hir_map,
analysis,
resolutions,
- &arenas,
+ &mut arenas,
&crate_name,
&outputs,
|tcx, analysis, rx, result| {
pub output_filenames: Option<&'a OutputFilenames>,
pub out_dir: Option<&'a Path>,
pub out_file: Option<&'a Path>,
- pub arenas: Option<&'tcx AllArenas<'tcx>>,
pub expanded_crate: Option<&'a ast::Crate>,
pub hir_crate: Option<&'a hir::Crate>,
pub hir_map: Option<&'a hir_map::Map<'tcx>>,
session,
out_dir: out_dir.as_ref().map(|s| &**s),
out_file: None,
- arenas: None,
krate: None,
registry: None,
cstore: None,
session: &'tcx Session,
out_dir: &'a Option<PathBuf>,
out_file: &'a Option<PathBuf>,
- arenas: &'tcx AllArenas<'tcx>,
cstore: &'tcx CStore,
hir_map: &'a hir_map::Map<'tcx>,
analysis: &'a ty::CrateAnalysis,
) -> Self {
CompileState {
crate_name: Some(crate_name),
- arenas: Some(arenas),
cstore: Some(cstore),
hir_map: Some(hir_map),
analysis: Some(analysis),
hir_map: hir_map::Map<'tcx>,
mut analysis: ty::CrateAnalysis,
resolutions: Resolutions,
- arenas: &'tcx AllArenas<'tcx>,
+ arenas: &'tcx mut AllArenas<'tcx>,
name: &str,
output_filenames: &OutputFilenames,
f: F,
#![feature(rustc_diagnostic_macros)]
#![feature(slice_sort_by_cached_key)]
#![feature(set_stdio)]
-#![feature(rustc_stack_internals)]
#![feature(no_debug)]
#![recursion_limit="256"]
&state.expanded_crate.take().unwrap(),
state.crate_name.unwrap(),
ppm,
- state.arenas.unwrap(),
state.output_filenames.unwrap(),
opt_uii.clone(),
state.out_file);
where F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
- #[cfg(all(unix, not(target_os = "haiku")))]
- let spawn_thread = unsafe {
- // Fetch the current resource limits
- let mut rlim = libc::rlimit {
- rlim_cur: 0,
- rlim_max: 0,
- };
- if libc::getrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
- let err = io::Error::last_os_error();
- error!("in_rustc_thread: error calling getrlimit: {}", err);
- true
- } else if rlim.rlim_max < STACK_SIZE as libc::rlim_t {
- true
- } else if rlim.rlim_cur < STACK_SIZE as libc::rlim_t {
- std::rt::deinit_stack_guard();
- rlim.rlim_cur = STACK_SIZE as libc::rlim_t;
- if libc::setrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
- let err = io::Error::last_os_error();
- error!("in_rustc_thread: error calling setrlimit: {}", err);
- std::rt::update_stack_guard();
- true
- } else {
- std::rt::update_stack_guard();
- false
- }
- } else {
- false
- }
- };
-
- // We set the stack size at link time. See src/rustc/rustc.rs.
- #[cfg(windows)]
- let spawn_thread = false;
-
- #[cfg(target_os = "haiku")]
- let spawn_thread = unsafe {
- // Haiku does not have setrlimit implemented for the stack size.
- // By default it does have the 16 MB stack limit, but we check this in
- // case the minimum STACK_SIZE changes or Haiku's defaults change.
- let mut rlim = libc::rlimit {
- rlim_cur: 0,
- rlim_max: 0,
- };
- if libc::getrlimit(libc::RLIMIT_STACK, &mut rlim) != 0 {
- let err = io::Error::last_os_error();
- error!("in_rustc_thread: error calling getrlimit: {}", err);
- true
- } else if rlim.rlim_cur >= STACK_SIZE {
- false
- } else {
- true
- }
- };
-
- #[cfg(not(any(windows, unix)))]
- let spawn_thread = true;
-
- // The or condition is added from backward compatibility.
- if spawn_thread || env::var_os("RUST_MIN_STACK").is_some() {
+ // We need a thread for soundness of thread local storage in rustc. For debugging purposes
+ // we allow an escape hatch where everything runs on the main thread.
+ if env::var_os("RUSTC_UNSTABLE_NO_MAIN_THREAD").is_none() {
let mut cfg = thread::Builder::new().name(name);
- // FIXME: Hacks on hacks. If the env is trying to override the stack size
- // then *don't* set it explicitly.
+ // If the env is trying to override the stack size then *don't* set it explicitly.
+ // The libstd thread impl will fetch the `RUST_MIN_STACK` env var itself.
if env::var_os("RUST_MIN_STACK").is_none() {
cfg = cfg.stack_size(STACK_SIZE);
}
hir_map: &hir_map::Map<'tcx>,
analysis: &ty::CrateAnalysis,
resolutions: &Resolutions,
- arenas: &'tcx AllArenas<'tcx>,
output_filenames: &OutputFilenames,
id: &str,
f: F
PpmTyped => {
let control = &driver::CompileController::basic();
let codegen_backend = ::get_codegen_backend(sess);
+ let mut arenas = AllArenas::new();
abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
control,
sess,
hir_map.clone(),
analysis.clone(),
resolutions.clone(),
- arenas,
+ &mut arenas,
id,
output_filenames,
|tcx, _, _, _| {
krate: &ast::Crate,
crate_name: &str,
ppm: PpMode,
- arenas: &'tcx AllArenas<'tcx>,
output_filenames: &OutputFilenames,
opt_uii: Option<UserIdentifiedItem>,
ofile: Option<&Path>) {
analysis,
resolutions,
crate_name,
- arenas,
output_filenames,
ppm,
opt_uii,
hir_map,
analysis,
resolutions,
- arenas,
output_filenames,
crate_name,
move |annotation, krate| {
hir_map,
analysis,
resolutions,
- arenas,
output_filenames,
crate_name,
move |_annotation, krate| {
hir_map,
analysis,
resolutions,
- arenas,
output_filenames,
crate_name,
move |annotation, _| {
hir_map,
analysis,
resolutions,
- arenas,
output_filenames,
crate_name,
move |_annotation, _krate| {
analysis: &ty::CrateAnalysis,
resolutions: &Resolutions,
crate_name: &str,
- arenas: &'tcx AllArenas<'tcx>,
output_filenames: &OutputFilenames,
ppm: PpMode,
uii: Option<UserIdentifiedItem>,
let control = &driver::CompileController::basic();
let codegen_backend = ::get_codegen_backend(sess);
+ let mut arenas = AllArenas::new();
abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
control,
sess,
hir_map.clone(),
analysis.clone(),
resolutions.clone(),
- arenas,
+ &mut arenas,
crate_name,
output_filenames,
|tcx, _, _, _| {
).expect("phase 2 aborted")
};
- let arenas = ty::AllArenas::new();
+ let mut arenas = ty::AllArenas::new();
let hir_map = hir_map::map_crate(&sess, &cstore, &mut hir_forest, &defs);
// Run just enough stuff to build a tcx.
&cstore,
ty::query::Providers::default(),
ty::query::Providers::default(),
- &arenas,
+ &mut arenas,
resolutions,
hir_map,
OnDiskCache::new_empty(sess.source_map()),
// (forced to be `pub` due to its use as an associated type below.)
crate struct Flows<'b, 'gcx: 'tcx, 'tcx: 'b> {
- borrows: FlowAtLocation<Borrows<'b, 'gcx, 'tcx>>,
- pub uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
- pub ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
+ borrows: FlowAtLocation<'tcx, Borrows<'b, 'gcx, 'tcx>>,
+ pub uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
+ pub ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'gcx, 'tcx>>,
/// Polonius Output
pub polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
impl<'b, 'gcx, 'tcx> Flows<'b, 'gcx, 'tcx> {
crate fn new(
- borrows: FlowAtLocation<Borrows<'b, 'gcx, 'tcx>>,
- uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
- ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
+ borrows: FlowAtLocation<'tcx, Borrows<'b, 'gcx, 'tcx>>,
+ uninits: FlowAtLocation<'tcx, MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
+ ever_inits: FlowAtLocation<'tcx, EverInitializedPlaces<'b, 'gcx, 'tcx>>,
polonius_output: Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
) -> Self {
Flows {
mod mutability_errors;
mod path_utils;
crate mod place_ext;
-mod places_conflict;
+crate mod places_conflict;
mod prefixes;
mod used_muts;
StatementKind::Nop
| StatementKind::AscribeUserType(..)
| StatementKind::Retag { .. }
- | StatementKind::EscapeToRaw { .. }
| StatementKind::StorageLive(..) => {
// `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
// to borrow check.
place,
borrow.kind,
root_place,
- sd
+ sd,
+ places_conflict::PlaceConflictBias::Overlap,
) {
debug!("check_for_invalidation_at_exit({:?}): INVALID", place);
// FIXME: should be talking about the region lifetime instead
StatementKind::Nop |
StatementKind::AscribeUserType(..) |
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::StorageLive(..) => {
// `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
// to borrow check.
mir: &Mir<'tcx>,
location_table: &LocationTable,
param_env: ty::ParamEnv<'gcx>,
- flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
+ flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
borrow_set: &BorrowSet<'tcx>,
errors_buffer: &mut Vec<Diagnostic>,
typeck: &mut TypeChecker<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
elements: &Rc<RegionValueElements>,
- flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
+ flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
location_table: &LocationTable,
) {
typeck: &mut TypeChecker<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
elements: &Rc<RegionValueElements>,
- flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
+ flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
liveness_map: &NllLivenessMap,
location_table: &LocationTable,
/// Results of dataflow tracking which variables (and paths) have been
/// initialized.
- flow_inits: &'me mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
+ flow_inits: &'me mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
/// Index indicating where each variable is assigned, used, or
/// dropped.
location_table: &LocationTable,
borrow_set: &BorrowSet<'tcx>,
all_facts: &mut Option<AllFacts>,
- flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
+ flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
elements: &Rc<RegionValueElements>,
) -> MirTypeckResults<'tcx> {
| StatementKind::StorageDead(..)
| StatementKind::InlineAsm { .. }
| StatementKind::Retag { .. }
- | StatementKind::EscapeToRaw { .. }
| StatementKind::Nop => {}
}
}
}
}
None => {
- // FIXME(canndrew): This is_never should probably be an is_uninhabited
- if !sig.output().is_never() {
+ if !sig.output().conservative_is_privately_uninhabited(self.tcx()) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
borrowed.kind,
place,
access,
+ places_conflict::PlaceConflictBias::Overlap,
) {
debug!(
"each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
use rustc::ty::{self, TyCtxt};
use std::cmp::max;
+/// When checking if a place conflicts with another place, this enum is used to influence decisions
+/// where a place might be equal or disjoint with another place, such as if `a[i] == a[j]`.
+/// `PlaceConflictBias::Overlap` would bias toward assuming that `i` might equal `j` and that these
+/// places overlap. `PlaceConflictBias::NoOverlap` assumes that for the purposes of the predicate
+/// being run in the calling context, the conservative choice is to assume the compared indices
+/// are disjoint (and therefore, do not overlap).
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+crate enum PlaceConflictBias {
+ Overlap,
+ NoOverlap,
+}
+
+/// Helper function for checking if places conflict with a mutable borrow and deep access depth.
+/// This is used to check for places conflicting outside of the borrow checking code (such as in
+/// dataflow).
+crate fn places_conflict<'gcx, 'tcx>(
+ tcx: TyCtxt<'_, 'gcx, 'tcx>,
+ mir: &Mir<'tcx>,
+ borrow_place: &Place<'tcx>,
+ access_place: &Place<'tcx>,
+ bias: PlaceConflictBias,
+) -> bool {
+ borrow_conflicts_with_place(
+ tcx,
+ mir,
+ borrow_place,
+ BorrowKind::Mut { allow_two_phase_borrow: true },
+ access_place,
+ AccessDepth::Deep,
+ bias,
+ )
+}
+
+/// Checks whether the `borrow_place` conflicts with the `access_place` given a borrow kind and
+/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
+/// array indices, for example) should be interpreted - this depends on what the caller wants in
+/// order to make the conservative choice and preserve soundness.
pub(super) fn borrow_conflicts_with_place<'gcx, 'tcx>(
tcx: TyCtxt<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
borrow_kind: BorrowKind,
access_place: &Place<'tcx>,
access: AccessDepth,
+ bias: PlaceConflictBias,
) -> bool {
debug!(
- "borrow_conflicts_with_place({:?},{:?},{:?})",
- borrow_place, access_place, access
+ "borrow_conflicts_with_place({:?}, {:?}, {:?}, {:?})",
+ borrow_place, access_place, access, bias,
);
// This Local/Local case is handled by the more general code below, but
borrow_components,
borrow_kind,
access_components,
- access
+ access,
+ bias,
)
})
})
borrow_kind: BorrowKind,
mut access_components: PlaceComponentsIter<'_, 'tcx>,
access: AccessDepth,
+ bias: PlaceConflictBias,
) -> bool {
// The borrowck rules for proving disjointness are applied from the "root" of the
// borrow forwards, iterating over "similar" projections in lockstep until
// check whether the components being borrowed vs
// accessed are disjoint (as in the second example,
// but not the first).
- match place_element_conflict(tcx, mir, borrow_c, access_c) {
+ match place_element_conflict(tcx, mir, borrow_c, access_c, bias) {
Overlap::Arbitrary => {
// We have encountered different fields of potentially
// the same union - the borrow now partially overlaps.
bug!("Tracking borrow behind shared reference.");
}
(ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
- // Values behind a mutatble reference are not access either by Dropping a
+ // Values behind a mutable reference are not access either by dropping a
// value, or by StorageDead
debug!("borrow_conflicts_with_place: drop access behind ptr");
return false;
mir: &Mir<'tcx>,
elem1: &Place<'tcx>,
elem2: &Place<'tcx>,
+ bias: PlaceConflictBias,
) -> Overlap {
match (elem1, elem2) {
(Place::Local(l1), Place::Local(l2)) => {
| (ProjectionElem::ConstantIndex { .. }, ProjectionElem::Index(..))
| (ProjectionElem::Subslice { .. }, ProjectionElem::Index(..)) => {
// Array indexes (`a[0]` vs. `a[i]`). These can either be disjoint
- // (if the indexes differ) or equal (if they are the same), so this
- // is the recursive case that gives "equal *or* disjoint" its meaning.
- debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
- Overlap::EqualOrDisjoint
+ // (if the indexes differ) or equal (if they are the same).
+ match bias {
+ PlaceConflictBias::Overlap => {
+ // If we are biased towards overlapping, then this is the recursive
+ // case that gives "equal *or* disjoint" its meaning.
+ debug!("place_element_conflict: DISJOINT-OR-EQ-ARRAY-INDEX");
+ Overlap::EqualOrDisjoint
+ }
+ PlaceConflictBias::NoOverlap => {
+ // If we are biased towards no overlapping, then this is disjoint.
+ debug!("place_element_conflict: DISJOINT-ARRAY-INDEX");
+ Overlap::Disjoint
+ }
+ }
}
(ProjectionElem::ConstantIndex { offset: o1, min_length: _, from_end: false },
ProjectionElem::ConstantIndex { offset: o2, min_length: _, from_end: false })
exit_block.unit()
}
ExprKind::Call { ty, fun, args, from_hir_call } => {
- // FIXME(canndrew): This is_never should probably be an is_uninhabited
- let diverges = expr.ty.is_never();
let intrinsic = match ty.sty {
ty::FnDef(def_id, _) => {
let f = ty.fn_sig(this.hir.tcx());
func: fun,
args,
cleanup: Some(cleanup),
- destination: if diverges {
+ // FIXME(varkor): replace this with an uninhabitedness-based check.
+ // This requires getting access to the current module to call
+ // `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
+ destination: if expr.ty.is_never() {
None
} else {
Some((destination.clone(), success))
});
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
- this.cfg
- .push_assign(block, source_info, destination, rvalue);
+ this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}
};
let err = error_to_const_error(&ecx, error);
// errors in statics are always emitted as fatal errors
if tcx.is_static(def_id).is_some() {
- let err = err.report_as_error(ecx.tcx, "could not evaluate static initializer");
- // check that a static never produces `TooGeneric`
+ let reported_err = err.report_as_error(ecx.tcx,
+ "could not evaluate static initializer");
+ // Ensure that if the above error was either `TooGeneric` or `Reported`
+ // an error must be reported.
if tcx.sess.err_count() == 0 {
- span_bug!(ecx.tcx.span, "static eval failure didn't emit an error: {:#?}", err);
+ tcx.sess.delay_span_bug(err.span,
+ &format!("static eval failure did not emit an error: {:#?}",
+ reported_err));
}
- err
+ reported_err
} else if def_id.is_local() {
// constant defined in this crate, we can figure out a lint level!
match tcx.describe_def(def_id) {
/// (e.g., via `reconstruct_statement_effect` and
/// `reconstruct_terminator_effect`; don't forget to call
/// `apply_local_effect`).
-pub struct FlowAtLocation<BD>
+pub struct FlowAtLocation<'tcx, BD>
where
- BD: BitDenotation,
+ BD: BitDenotation<'tcx>,
{
- base_results: DataflowResults<BD>,
+ base_results: DataflowResults<'tcx, BD>,
curr_state: BitSet<BD::Idx>,
stmt_gen: HybridBitSet<BD::Idx>,
stmt_kill: HybridBitSet<BD::Idx>,
}
-impl<BD> FlowAtLocation<BD>
+impl<'tcx, BD> FlowAtLocation<'tcx, BD>
where
- BD: BitDenotation,
+ BD: BitDenotation<'tcx>,
{
/// Iterate over each bit set in the current state.
pub fn each_state_bit<F>(&self, f: F)
self.stmt_gen.iter().for_each(f)
}
- pub fn new(results: DataflowResults<BD>) -> Self {
+ pub fn new(results: DataflowResults<'tcx, BD>) -> Self {
let bits_per_block = results.sets().bits_per_block();
let curr_state = BitSet::new_empty(bits_per_block);
let stmt_gen = HybridBitSet::new_empty(bits_per_block);
}
}
-impl<BD> FlowsAtLocation for FlowAtLocation<BD>
- where BD: BitDenotation
+impl<'tcx, BD> FlowsAtLocation for FlowAtLocation<'tcx, BD>
+ where BD: BitDenotation<'tcx>
{
fn reset_to_entry_of(&mut self, bb: BasicBlock) {
self.curr_state.overwrite(self.base_results.sets().on_entry_set_for(bb.index()));
}
-impl<'tcx, T> FlowAtLocation<T>
+impl<'tcx, T> FlowAtLocation<'tcx, T>
where
- T: HasMoveData<'tcx> + BitDenotation<Idx = MovePathIndex>,
+ T: HasMoveData<'tcx> + BitDenotation<'tcx, Idx = MovePathIndex>,
{
pub fn has_any_child_of(&self, mpi: T::Idx) -> Option<T::Idx> {
// We process `mpi` before the loop below, for two reasons:
use super::DebugFormatted;
pub trait MirWithFlowState<'tcx> {
- type BD: BitDenotation;
+ type BD: BitDenotation<'tcx>;
fn node_id(&self) -> NodeId;
fn mir(&self) -> &Mir<'tcx>;
- fn flow_state(&self) -> &DataflowState<Self::BD>;
+ fn flow_state(&self) -> &DataflowState<'tcx, Self::BD>;
}
impl<'a, 'tcx, BD> MirWithFlowState<'tcx> for DataflowBuilder<'a, 'tcx, BD>
- where BD: BitDenotation
+ where BD: BitDenotation<'tcx>
{
type BD = BD;
fn node_id(&self) -> NodeId { self.node_id }
fn mir(&self) -> &Mir<'tcx> { self.flow_state.mir() }
- fn flow_state(&self) -> &DataflowState<Self::BD> { &self.flow_state.flow_state }
+ fn flow_state(&self) -> &DataflowState<'tcx, Self::BD> { &self.flow_state.flow_state }
}
struct Graph<'a, 'tcx, MWF:'a, P> where
path: &Path,
render_idx: P)
-> io::Result<()>
- where BD: BitDenotation,
- P: Fn(&BD, BD::Idx) -> DebugFormatted
+ where BD: BitDenotation<'tcx>,
+ P: Fn(&BD, BD::Idx) -> DebugFormatted,
{
let g = Graph { mbcx, phantom: PhantomData, render_idx };
let mut v = Vec::new();
impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P>
where MWF: MirWithFlowState<'tcx>,
- P: Fn(&MWF::BD, <MWF::BD as BitDenotation>::Idx) -> DebugFormatted,
+ P: Fn(&MWF::BD, <MWF::BD as BitDenotation<'tcx>>::Idx) -> DebugFormatted,
{
type Node = Node;
type Edge = Edge;
impl<'a, 'tcx, MWF, P> Graph<'a, 'tcx, MWF, P>
where MWF: MirWithFlowState<'tcx>,
- P: Fn(&MWF::BD, <MWF::BD as BitDenotation>::Idx) -> DebugFormatted,
+ P: Fn(&MWF::BD, <MWF::BD as BitDenotation<'tcx>>::Idx) -> DebugFormatted,
{
/// Generate the node label
fn node_label_internal<W: io::Write>(&self,
}
}
-impl<'a, 'tcx> BitDenotation for HaveBeenBorrowedLocals<'a, 'tcx> {
+impl<'a, 'tcx> BitDenotation<'tcx> for HaveBeenBorrowedLocals<'a, 'tcx> {
type Idx = Local;
fn name() -> &'static str { "has_been_borrowed_locals" }
fn bits_per_block(&self) -> usize {
}.visit_terminator(loc.block, self.mir[loc.block].terminator(), loc);
}
- fn propagate_call_return(&self,
- _in_out: &mut BitSet<Local>,
- _call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- _dest_place: &mir::Place) {
+ fn propagate_call_return(
+ &self,
+ _in_out: &mut BitSet<Local>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ _dest_place: &mir::Place<'tcx>,
+ ) {
// Nothing to do when a call returns successfully
}
}
use borrow_check::borrow_set::{BorrowSet, BorrowData};
use borrow_check::place_ext::PlaceExt;
-use rustc;
use rustc::mir::{self, Location, Place, Mir};
use rustc::ty::TyCtxt;
use rustc::ty::RegionVid;
pub use dataflow::indexes::BorrowIndex;
use borrow_check::nll::region_infer::RegionInferenceContext;
use borrow_check::nll::ToRegionVid;
+use borrow_check::places_conflict;
use std::rc::Rc;
}
}
- fn kill_borrows_on_local(&self,
- sets: &mut BlockSets<BorrowIndex>,
- local: &rustc::mir::Local)
- {
- if let Some(borrow_indexes) = self.borrow_set.local_map.get(local) {
- sets.kill_all(borrow_indexes);
+ /// Kill any borrows that conflict with `place`.
+ fn kill_borrows_on_place(
+ &self,
+ sets: &mut BlockSets<BorrowIndex>,
+ place: &Place<'tcx>
+ ) {
+ debug!("kill_borrows_on_place: place={:?}", place);
+ // Handle the `Place::Local(..)` case first and exit early.
+ if let Place::Local(local) = place {
+ if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
+ debug!("kill_borrows_on_place: borrow_indices={:?}", borrow_indices);
+ sets.kill_all(borrow_indices);
+ return;
+ }
+ }
+
+ // Otherwise, look at all borrows that are live and if they conflict with the assignment
+ // into our place then we can kill them.
+ let mut borrows = sets.on_entry.clone();
+ let _ = borrows.union(sets.gen_set);
+ for borrow_index in borrows.iter() {
+ let borrow_data = &self.borrows()[borrow_index];
+ debug!(
+ "kill_borrows_on_place: borrow_index={:?} borrow_data={:?}",
+ borrow_index, borrow_data,
+ );
+
+ // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
+ // pair of array indices are unequal, so that when `places_conflict` returns true, we
+ // will be assured that two places being compared definitely denotes the same sets of
+ // locations.
+ if places_conflict::places_conflict(
+ self.tcx,
+ self.mir,
+ place,
+ &borrow_data.borrowed_place,
+ places_conflict::PlaceConflictBias::NoOverlap,
+ ) {
+ debug!(
+ "kill_borrows_on_place: (kill) borrow_index={:?} borrow_data={:?}",
+ borrow_index, borrow_data,
+ );
+ sets.kill(borrow_index);
+ }
}
}
}
-impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for Borrows<'a, 'gcx, 'tcx> {
type Idx = BorrowIndex;
fn name() -> &'static str { "borrows" }
fn bits_per_block(&self) -> usize {
}
fn statement_effect(&self, sets: &mut BlockSets<BorrowIndex>, location: Location) {
- debug!("Borrows::statement_effect sets: {:?} location: {:?}", sets, location);
+ debug!("Borrows::statement_effect: sets={:?} location={:?}", sets, location);
let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
panic!("could not find block at location {:?}", location);
panic!("could not find statement at location {:?}");
});
+ debug!("Borrows::statement_effect: stmt={:?}", stmt);
match stmt.kind {
mir::StatementKind::Assign(ref lhs, ref rhs) => {
// Make sure there are no remaining borrows for variables
// that are assigned over.
- if let Place::Local(ref local) = *lhs {
- // FIXME: Handle the case in which we're assigning over
- // a projection (`foo.bar`).
- self.kill_borrows_on_local(sets, local);
- }
-
- // NOTE: if/when the Assign case is revised to inspect
- // the assigned_place here, make sure to also
- // re-consider the current implementations of the
- // propagate_call_return method.
+ self.kill_borrows_on_place(sets, lhs);
if let mir::Rvalue::Ref(_, _, ref place) = **rhs {
if place.ignore_borrow(
mir::StatementKind::StorageDead(local) => {
// Make sure there are no remaining borrows for locals that
// are gone out of scope.
- self.kill_borrows_on_local(sets, &local)
+ self.kill_borrows_on_place(sets, &Place::Local(local));
}
mir::StatementKind::InlineAsm { ref outputs, ref asm, .. } => {
for (output, kind) in outputs.iter().zip(&asm.outputs) {
if !kind.is_indirect && !kind.is_rw {
- // Make sure there are no remaining borrows for direct
- // output variables.
- if let Place::Local(ref local) = *output {
- // FIXME: Handle the case in which we're assigning over
- // a projection (`foo.bar`).
- self.kill_borrows_on_local(sets, local);
- }
+ self.kill_borrows_on_place(sets, output);
}
}
}
mir::StatementKind::SetDiscriminant { .. } |
mir::StatementKind::StorageLive(..) |
mir::StatementKind::Retag { .. } |
- mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => {}
fn terminator_effect(&self, _: &mut BlockSets<BorrowIndex>, _: Location) {}
- fn propagate_call_return(&self,
- _in_out: &mut BitSet<BorrowIndex>,
- _call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- _dest_place: &mir::Place) {
- // there are no effects on borrows from method call return...
- //
- // ... but if overwriting a place can affect flow state, then
- // latter is not true; see NOTE on Assign case in
- // statement_effect_on_borrows.
+ fn propagate_call_return(
+ &self,
+ _in_out: &mut BitSet<BorrowIndex>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ _dest_place: &mir::Place<'tcx>,
+ ) {
}
}
false // bottom = nothing is reserved or activated yet
}
}
-
}
}
-impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex;
fn name() -> &'static str { "maybe_init" }
fn bits_per_block(&self) -> usize {
)
}
- fn propagate_call_return(&self,
- in_out: &mut BitSet<MovePathIndex>,
- _call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- dest_place: &mir::Place) {
+ fn propagate_call_return(
+ &self,
+ in_out: &mut BitSet<MovePathIndex>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ dest_place: &mir::Place<'tcx>,
+ ) {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 1 (initialized).
on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
}
}
-impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex;
fn name() -> &'static str { "maybe_uninit" }
fn bits_per_block(&self) -> usize {
)
}
- fn propagate_call_return(&self,
- in_out: &mut BitSet<MovePathIndex>,
- _call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- dest_place: &mir::Place) {
+ fn propagate_call_return(
+ &self,
+ in_out: &mut BitSet<MovePathIndex>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ dest_place: &mir::Place<'tcx>,
+ ) {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 0 (initialized).
on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
}
}
-impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = MovePathIndex;
fn name() -> &'static str { "definite_init" }
fn bits_per_block(&self) -> usize {
)
}
- fn propagate_call_return(&self,
- in_out: &mut BitSet<MovePathIndex>,
- _call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- dest_place: &mir::Place) {
+ fn propagate_call_return(
+ &self,
+ in_out: &mut BitSet<MovePathIndex>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ dest_place: &mir::Place<'tcx>,
+ ) {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 1 (initialized).
on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
}
}
-impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedPlaces<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
type Idx = InitIndex;
fn name() -> &'static str { "ever_init" }
fn bits_per_block(&self) -> usize {
);
}
- fn propagate_call_return(&self,
- in_out: &mut BitSet<InitIndex>,
- call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- _dest_place: &mir::Place) {
+ fn propagate_call_return(
+ &self,
+ in_out: &mut BitSet<InitIndex>,
+ call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ _dest_place: &mir::Place<'tcx>,
+ ) {
let move_data = self.move_data();
let bits_per_block = self.bits_per_block();
let init_loc_map = &move_data.init_loc_map;
}
}
-impl<'a, 'tcx> BitDenotation for MaybeStorageLive<'a, 'tcx> {
+impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> {
type Idx = Local;
fn name() -> &'static str { "maybe_storage_live" }
fn bits_per_block(&self) -> usize {
// Terminators have no effect
}
- fn propagate_call_return(&self,
- _in_out: &mut BitSet<Local>,
- _call_bb: mir::BasicBlock,
- _dest_bb: mir::BasicBlock,
- _dest_place: &mir::Place) {
+ fn propagate_call_return(
+ &self,
+ _in_out: &mut BitSet<Local>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ _dest_place: &mir::Place<'tcx>,
+ ) {
// Nothing to do when a call returns successfully
}
}
pub(crate) use self::move_paths::indexes;
-pub(crate) struct DataflowBuilder<'a, 'tcx: 'a, BD> where BD: BitDenotation
+pub(crate) struct DataflowBuilder<'a, 'tcx: 'a, BD>
+where
+ BD: BitDenotation<'tcx>
{
node_id: ast::NodeId,
flow_state: DataflowAnalysis<'a, 'tcx, BD>,
}
}
-pub(crate) trait Dataflow<BD: BitDenotation> {
+pub(crate) trait Dataflow<'tcx, BD: BitDenotation<'tcx>> {
/// Sets up and runs the dataflow problem, using `p` to render results if
/// implementation so chooses.
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> DebugFormatted {
fn propagate(&mut self);
}
-impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation
+impl<'a, 'tcx: 'a, BD> Dataflow<'tcx, BD> for DataflowBuilder<'a, 'tcx, BD>
+where
+ BD: BitDenotation<'tcx>
{
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> DebugFormatted {
self.flow_state.build_sets();
dead_unwinds: &BitSet<BasicBlock>,
bd: BD,
p: P)
- -> DataflowResults<BD>
- where BD: BitDenotation + InitialFlow,
+ -> DataflowResults<'tcx, BD>
+ where BD: BitDenotation<'tcx> + InitialFlow,
P: Fn(&BD, BD::Idx) -> DebugFormatted
{
let flow_state = DataflowAnalysis::new(mir, dead_unwinds, bd);
flow_state.run(tcx, node_id, attributes, p)
}
-impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
+impl<'a, 'gcx: 'tcx, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation<'tcx>
{
pub(crate) fn run<P>(self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
node_id: ast::NodeId,
attributes: &[ast::Attribute],
- p: P) -> DataflowResults<BD>
+ p: P) -> DataflowResults<'tcx, BD>
where P: Fn(&BD, BD::Idx) -> DebugFormatted
{
let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option<String> {
}
}
-struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> where O: 'b + BitDenotation
+struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> where O: 'b + BitDenotation<'tcx>
{
builder: &'b mut DataflowAnalysis<'a, 'tcx, O>,
}
-impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation
+impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation<'tcx>
{
fn propagate(&mut self) {
let mut temp = BitSet::new_empty(self.flow_state.sets.bits_per_block);
}
}
-impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation
+impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation<'tcx>
{
fn walk_cfg(&mut self, in_out: &mut BitSet<BD::Idx>) {
let mut dirty_queue: WorkQueue<mir::BasicBlock> =
path
}
-impl<'a, 'tcx: 'a, BD> DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation
+impl<'a, 'tcx: 'a, BD> DataflowBuilder<'a, 'tcx, BD> where BD: BitDenotation<'tcx>
{
fn pre_dataflow_instrumentation<P>(&self, p: P) -> io::Result<()>
where P: Fn(&BD, BD::Idx) -> DebugFormatted
fn mir(&self) -> &'a Mir<'tcx>;
}
-pub fn state_for_location<'tcx, T: BitDenotation>(loc: Location,
- analysis: &T,
- result: &DataflowResults<T>,
- mir: &Mir<'tcx>)
+pub fn state_for_location<'tcx, T: BitDenotation<'tcx>>(loc: Location,
+ analysis: &T,
+ result: &DataflowResults<'tcx, T>,
+ mir: &Mir<'tcx>)
-> BitSet<T::Idx> {
let mut on_entry = result.sets().on_entry_set_for(loc.block.index()).to_owned();
let mut kill_set = on_entry.to_hybrid();
gen_set.to_dense()
}
-pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation
+pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation<'tcx>
{
- flow_state: DataflowState<O>,
+ flow_state: DataflowState<'tcx, O>,
dead_unwinds: &'a BitSet<mir::BasicBlock>,
mir: &'a Mir<'tcx>,
}
-impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> where O: BitDenotation
+impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> where O: BitDenotation<'tcx>
{
- pub fn results(self) -> DataflowResults<O> {
+ pub fn results(self) -> DataflowResults<'tcx, O> {
DataflowResults(self.flow_state)
}
pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
}
-pub struct DataflowResults<O>(pub(crate) DataflowState<O>) where O: BitDenotation;
+pub struct DataflowResults<'tcx, O>(pub(crate) DataflowState<'tcx, O>) where O: BitDenotation<'tcx>;
-impl<O: BitDenotation> DataflowResults<O> {
+impl<'tcx, O: BitDenotation<'tcx>> DataflowResults<'tcx, O> {
pub fn sets(&self) -> &AllSets<O::Idx> {
&self.0.sets
}
/// State of a dataflow analysis; couples a collection of bit sets
/// with operator used to initialize and merge bits during analysis.
-pub struct DataflowState<O: BitDenotation>
+pub struct DataflowState<'tcx, O: BitDenotation<'tcx>>
{
/// All the sets for the analysis. (Factored into its
/// own structure so that we can borrow it mutably
pub(crate) operator: O,
}
-impl<O: BitDenotation> DataflowState<O> {
+impl<'tcx, O: BitDenotation<'tcx>> DataflowState<'tcx, O> {
pub(crate) fn interpret_set<'c, P>(&self,
o: &'c O,
set: &BitSet<O::Idx>,
fn bottom_value() -> bool;
}
-pub trait BitDenotation: BitSetOperator {
+pub trait BitDenotation<'tcx>: BitSetOperator {
/// Specifies what index type is used to access the bitvector.
type Idx: Idx;
/// be better to represent this as an additional gen- and
/// kill-sets associated with each edge coming out of the basic
/// block.
- fn propagate_call_return(&self,
- in_out: &mut BitSet<Self::Idx>,
- call_bb: mir::BasicBlock,
- dest_bb: mir::BasicBlock,
- dest_place: &mir::Place);
+ fn propagate_call_return(
+ &self,
+ in_out: &mut BitSet<Self::Idx>,
+ call_bb: mir::BasicBlock,
+ dest_bb: mir::BasicBlock,
+ dest_place: &mir::Place<'tcx>,
+ );
}
-impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
+impl<'a, 'tcx, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation<'tcx>
{
pub fn new(mir: &'a Mir<'tcx>,
dead_unwinds: &'a BitSet<mir::BasicBlock>,
}
}
-impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation
-{
+impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> where D: BitDenotation<'tcx> {
/// Propagates the bits of `in_out` into all the successors of `bb`,
/// using bitwise operator denoted by `self.operator`.
///
fn propagate_bits_into_graph_successors_of(
&mut self,
in_out: &mut BitSet<D::Idx>,
- (bb, bb_data): (mir::BasicBlock, &mir::BasicBlockData),
+ (bb, bb_data): (mir::BasicBlock, &mir::BasicBlockData<'tcx>),
dirty_list: &mut WorkQueue<mir::BasicBlock>)
{
match bb_data.terminator().kind {
"SetDiscriminant should not exist during borrowck");
}
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
}
let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns {
self.tcx.is_ty_uninhabited_from(module, pat_ty)
} else {
- self.conservative_is_uninhabited(pat_ty)
+ match pat_ty.sty {
+ ty::Never => true,
+ ty::Adt(def, _) => def.variants.is_empty(),
+ _ => false
+ }
};
if !scrutinee_is_uninhabited {
// We know the type is inhabited, so this must be wrong
})
}
- fn conservative_is_uninhabited(&self, scrutinee_ty: Ty<'tcx>) -> bool {
- // "rustc-1.0-style" uncontentious uninhabitableness check
- match scrutinee_ty.sty {
- ty::Never => true,
- ty::Adt(def, _) => def.variants.is_empty(),
- _ => false
- }
- }
-
fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str) {
let module = self.tcx.hir().get_module_parent(pat.id);
MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| {
}
Misc => {
- let src_layout = src.layout;
let src = self.read_immediate(src)?;
- // There are no casts to references
- assert!(!dest.layout.ty.is_region_ptr());
- // Hence we make all casts erase the tag
- let src = src.erase_tag().with_default_tag();
-
- if self.type_is_fat_ptr(src_layout.ty) {
- match (src, self.type_is_fat_ptr(dest.layout.ty)) {
+ if self.type_is_fat_ptr(src.layout.ty) {
+ match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
// pointers to extern types
(Immediate::Scalar(_),_) |
// slices and trait objects to other slices/trait objects
(Immediate::ScalarPair(..), true) => {
// No change to immediate
- self.write_immediate(src, dest)?;
+ self.write_immediate(*src, dest)?;
}
// slices and trait objects to thin pointers (dropping the metadata)
(Immediate::ScalarPair(data, _), false) => {
}
}
} else {
- match src_layout.variants {
+ match src.layout.variants {
layout::Variants::Single { index } => {
- if let Some(def) = src_layout.ty.ty_adt_def() {
+ if let Some(def) = src.layout.ty.ty_adt_def() {
// Cast from a univariant enum
- assert!(src_layout.is_zst());
+ assert!(src.layout.is_zst());
let discr_val = def
.discriminant_for_variant(*self.tcx, index)
.val;
layout::Variants::NicheFilling { .. } => {},
}
- let dest_val = self.cast_scalar(src.to_scalar()?, src_layout, dest.layout)?;
+ let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
self.write_scalar(dest_val, dest)?;
}
}
#[inline]
fn retag(
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
- _fn_entry: bool,
- _two_phase: bool,
+ _kind: mir::RetagKind,
_place: PlaceTy<'tcx, Self::PointerTag>,
) -> EvalResult<'tcx> {
Ok(())
}
- /// Execute an escape-to-raw operation
- #[inline]
- fn escape_to_raw(
- _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
- _ptr: OpTy<'tcx, Self::PointerTag>,
- ) -> EvalResult<'tcx> {
- Ok(())
- }
-
/// Called immediately before a new stack frame got pushed
fn stack_push(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
FakeRead(..) => {}
// Stacked Borrows.
- Retag { fn_entry, two_phase, ref place } => {
+ Retag(kind, ref place) => {
let dest = self.eval_place(place)?;
- M::retag(self, fn_entry, two_phase, dest)?;
- }
- EscapeToRaw(ref op) => {
- let op = self.eval_operand(op, None)?;
- M::escape_to_raw(self, op)?;
+ M::retag(self, kind, dest)?;
}
// Statements we do not track.
// The first argument (index 0), but add 1 for the return value.
let dropee_ptr = Place::Local(Local::new(1+0));
if tcx.sess.opts.debugging_opts.mir_emit_retag {
- // Function arguments should be retagged
+ // Function arguments should be retagged, and we make this one raw.
mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
source_info,
- kind: StatementKind::Retag {
- fn_entry: true,
- two_phase: false,
- place: dropee_ptr.clone(),
- },
+ kind: StatementKind::Retag(RetagKind::Raw, dropee_ptr.clone()),
});
- // We use raw ptr operations, better prepare the alias tracking for that
- mir.basic_blocks_mut()[START_BLOCK].statements.insert(1, Statement {
- source_info,
- kind: StatementKind::EscapeToRaw(Operand::Copy(dropee_ptr.clone())),
- })
}
let patch = {
let param_env = tcx.param_env(def_id).with_reveal_all();
basic_blocks[START_BLOCK].statements.splice(0..0,
places.into_iter().map(|place| Statement {
source_info,
- kind: StatementKind::Retag { fn_entry: true, two_phase: false, place },
+ kind: StatementKind::Retag(RetagKind::FnEntry, place),
})
);
}
for (source_info, dest_place, dest_block) in returns {
basic_blocks[dest_block].statements.insert(0, Statement {
source_info,
- kind: StatementKind::Retag { fn_entry: false, two_phase: false, place: dest_place },
+ kind: StatementKind::Retag(RetagKind::Default, dest_place),
});
}
// We want to insert statements as we iterate. To this end, we
// iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
- match block_data.statements[i].kind {
- // If we are casting *from* a reference, we may have to escape-to-raw.
- StatementKind::Assign(_, box Rvalue::Cast(
+ let (retag_kind, place) = match block_data.statements[i].kind {
+ // If we are casting *from* a reference, we may have to retag-as-raw.
+ StatementKind::Assign(ref place, box Rvalue::Cast(
CastKind::Misc,
ref src,
dest_ty,
if src_ty.is_region_ptr() {
// The only `Misc` casts on references are those creating raw pointers.
assert!(dest_ty.is_unsafe_ptr());
- // Insert escape-to-raw before the cast. We are not concerned
- // with stability here: Our EscapeToRaw will not change the value
- // that the cast will then use.
- // `src` might be a "move", but we rely on this not actually moving
- // but just doing a memcpy. It is crucial that we do EscapeToRaw
- // on the src because we need it with its original type.
- let source_info = block_data.statements[i].source_info;
- block_data.statements.insert(i, Statement {
- source_info,
- kind: StatementKind::EscapeToRaw(src.clone()),
- });
+ (RetagKind::Raw, place)
+ } else {
+ // Some other cast, no retag
+ continue
}
}
// Assignments of reference or ptr type are the ones where we may have
// to update tags. This includes `x = &[mut] ...` and hence
// we also retag after taking a reference!
StatementKind::Assign(ref place, box ref rvalue) if needs_retag(place) => {
- let two_phase = match rvalue {
- Rvalue::Ref(_, borrow_kind, _) =>
- borrow_kind.allows_two_phase_borrow(),
- _ => false
+ let kind = match rvalue {
+ Rvalue::Ref(_, borrow_kind, _)
+ if borrow_kind.allows_two_phase_borrow()
+ =>
+ RetagKind::TwoPhase,
+ _ =>
+ RetagKind::Default,
};
- // Insert a retag after the assignment.
- let source_info = block_data.statements[i].source_info;
- block_data.statements.insert(i+1, Statement {
- source_info,
- kind: StatementKind::Retag {
- fn_entry: false,
- two_phase,
- place: place.clone(),
- },
- });
+ (kind, place)
}
// Do nothing for the rest
- _ => {},
+ _ => continue,
};
+ // Insert a retag after the statement.
+ let source_info = block_data.statements[i].source_info;
+ block_data.statements.insert(i+1, Statement {
+ source_info,
+ kind: StatementKind::Retag(retag_kind, place.clone()),
+ });
}
}
}
StatementKind::StorageLive(..) |
StatementKind::StorageDead(..) |
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {
// safe (at least as emitted during MIR construction)
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &'a Mir<'tcx>,
env: &'a MoveDataParamEnv<'tcx, 'tcx>,
- flow_inits: DataflowResults<MaybeInitializedPlaces<'a, 'tcx, 'tcx>>,
- flow_uninits: DataflowResults<MaybeUninitializedPlaces<'a, 'tcx, 'tcx>>,
+ flow_inits: DataflowResults<'tcx, MaybeInitializedPlaces<'a, 'tcx, 'tcx>>,
+ flow_uninits: DataflowResults<'tcx, MaybeUninitializedPlaces<'a, 'tcx, 'tcx>>,
drop_flags: FxHashMap<MovePathIndex, Local>,
patch: MirPatch<'tcx>,
}
// Alias tracking must know we changed the type
mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
source_info,
- kind: StatementKind::EscapeToRaw(Operand::Copy(Place::Local(self_arg()))),
+ kind: StatementKind::Retag(RetagKind::Raw, Place::Local(self_arg())),
})
}
fn visit_retag(
&mut self,
- fn_entry: &mut bool,
- two_phase: &mut bool,
+ kind: &mut RetagKind,
place: &mut Place<'tcx>,
loc: Location,
) {
- self.super_retag(fn_entry, two_phase, place, loc);
+ self.super_retag(kind, place, loc);
// We have to patch all inlined retags to be aware that they are no longer
// happening on function entry.
- *fn_entry = false;
+ if *kind == RetagKind::FnEntry {
+ *kind = RetagKind::Default;
+ }
}
fn visit_terminator_kind(&mut self, block: BasicBlock,
StatementKind::StorageDead(_) |
StatementKind::InlineAsm {..} |
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
}
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag { .. }
- | StatementKind::EscapeToRaw { .. }
| StatementKind::AscribeUserType(..)
| StatementKind::Nop => Ok(()),
}
StatementKind::Assign { .. } |
StatementKind::SetDiscriminant { .. } |
StatementKind::InlineAsm { .. } |
- StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } => {
+ StatementKind::Retag { .. } => {
return false;
}
}
mir: &Mir<'tcx>,
id: ast::NodeId,
_attributes: &[ast::Attribute],
- results: &DataflowResults<O>)
- where O: BitDenotation<Idx=MovePathIndex> + HasMoveData<'tcx>
+ results: &DataflowResults<'tcx, O>)
+ where O: BitDenotation<'tcx, Idx=MovePathIndex> + HasMoveData<'tcx>
{
debug!("sanity_check_via_rustc_peek id: {:?}", id);
// FIXME: this is not DRY. Figure out way to abstract this and
fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
- results: &DataflowResults<O>,
+ results: &DataflowResults<'tcx, O>,
bb: mir::BasicBlock) where
- O: BitDenotation<Idx=MovePathIndex> + HasMoveData<'tcx>
+ O: BitDenotation<'tcx, Idx=MovePathIndex> + HasMoveData<'tcx>
{
let move_data = results.0.operator.move_data();
let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = mir[bb];
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::Retag { .. } |
- mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => continue,
mir::StatementKind::SetDiscriminant{ .. } =>
}
visit::walk_attribute(self, attr);
}
+
+ fn visit_ident(&mut self, ident: Ident) {
+ if ident.name == keywords::DollarCrate.name() {
+ let name = match self.resolver.resolve_crate_root(ident).kind {
+ ModuleKind::Def(_, name) if name != keywords::Invalid.name() => name,
+ _ => keywords::Crate.name(),
+ };
+ ident.span.ctxt().set_dollar_crate_name(name);
+ }
+ visit::walk_ident(self, ident);
+ }
}
}
}
- fn is_local(&self) -> bool {
- self.normal_ancestor_id.is_local()
- }
-
fn nearest_item_scope(&'a self) -> Module<'a> {
if self.is_trait() { self.parent.unwrap() } else { self }
}
use {AmbiguityError, AmbiguityKind, AmbiguityErrorMisc};
use {CrateLint, Resolver, ResolutionError, ScopeSet, Weak};
-use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
+use {Module, NameBinding, NameBindingKind, PathResult, Segment, ToNameBinding};
use {is_known_tool, resolve_error};
use ModuleOrUniformRoot;
use Namespace::*;
use syntax::ext::hygiene::{self, Mark};
use syntax::ext::tt::macro_rules;
use syntax::feature_gate::{feature_err, is_builtin_attr_name, GateIssue};
-use syntax::fold::{self, Folder};
-use syntax::ptr::P;
use syntax::symbol::{Symbol, keywords};
use syntax::util::lev_distance::find_best_match_for_name;
use syntax_pos::{Span, DUMMY_SP};
mark
}
- fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> {
- struct EliminateCrateVar<'b, 'a: 'b>(
- &'b mut Resolver<'a>, Span
- );
-
- impl<'a, 'b> Folder for EliminateCrateVar<'a, 'b> {
- fn fold_path(&mut self, path: ast::Path) -> ast::Path {
- match self.fold_qpath(None, path) {
- (None, path) => path,
- _ => unreachable!(),
- }
- }
-
- fn fold_qpath(&mut self, mut qself: Option<ast::QSelf>, mut path: ast::Path)
- -> (Option<ast::QSelf>, ast::Path) {
- qself = qself.map(|ast::QSelf { ty, path_span, position }| {
- ast::QSelf {
- ty: self.fold_ty(ty),
- path_span: self.new_span(path_span),
- position,
- }
- });
-
- if path.segments[0].ident.name == keywords::DollarCrate.name() {
- let module = self.0.resolve_crate_root(path.segments[0].ident);
- path.segments[0].ident.name = keywords::PathRoot.name();
- if !module.is_local() {
- let span = path.segments[0].ident.span;
- path.segments.insert(1, match module.kind {
- ModuleKind::Def(_, name) => ast::PathSegment::from_ident(
- ast::Ident::with_empty_ctxt(name).with_span_pos(span)
- ),
- _ => unreachable!(),
- });
- if let Some(qself) = &mut qself {
- qself.position += 1;
- }
- }
- }
- (qself, path)
- }
-
- fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
- fold::noop_fold_mac(mac, self)
- }
- }
-
- let ret = EliminateCrateVar(self, item.span).fold_item(item);
- assert!(ret.len() == 1);
- ret.into_iter().next().unwrap()
- }
-
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
derives: &[Mark]) {
let invocation = self.invocations[&mark];
self.definitions.add_parent_module_of_macro_def(invoc.expansion_data.mark,
normal_module_def_id);
invoc.expansion_data.mark.set_default_transparency(ext.default_transparency());
- invoc.expansion_data.mark.set_is_builtin(def_id.krate == CrateNum::BuiltinMacros);
}
Ok(Some(ext))
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use astconv::AstConv;
-
use super::{FnCtxt, PlaceOp, Needs};
use super::method::MethodCallee;
-use rustc::infer::InferOk;
+use rustc::infer::{InferCtxt, InferOk};
use rustc::session::DiagnosticMessageId;
-use rustc::traits;
-use rustc::ty::{self, Ty, TraitRef};
+use rustc::traits::{self, TraitEngine};
+use rustc::ty::{self, Ty, TyCtxt, TraitRef};
use rustc::ty::{ToPredicate, TypeFoldable};
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
use syntax_pos::Span;
-use syntax::ast::Ident;
+use syntax::ast::{self, Ident};
use std::iter;
}
pub struct Autoderef<'a, 'gcx: 'tcx, 'tcx: 'a> {
- fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
+ infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+ body_id: ast::NodeId,
+ param_env: ty::ParamEnv<'tcx>,
steps: Vec<(Ty<'tcx>, AutoderefKind)>,
cur_ty: Ty<'tcx>,
obligations: Vec<traits::PredicateObligation<'tcx>>,
at_start: bool,
include_raw_pointers: bool,
span: Span,
+ silence_errors: bool,
+ reached_recursion_limit: bool
}
impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
type Item = (Ty<'tcx>, usize);
fn next(&mut self) -> Option<Self::Item> {
- let tcx = self.fcx.tcx;
+ let tcx = self.infcx.tcx;
debug!("autoderef: steps={:?}, cur_ty={:?}",
self.steps,
}
if self.steps.len() >= *tcx.sess.recursion_limit.get() {
- // We've reached the recursion limit, error gracefully.
- let suggested_limit = *tcx.sess.recursion_limit.get() * 2;
- let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`",
- self.cur_ty);
- let error_id = (DiagnosticMessageId::ErrorId(55), Some(self.span), msg);
- let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
- if fresh {
- struct_span_err!(tcx.sess,
- self.span,
- E0055,
- "reached the recursion limit while auto-dereferencing `{:?}`",
- self.cur_ty)
- .span_label(self.span, "deref recursion limit reached")
- .help(&format!(
- "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
- suggested_limit))
- .emit();
+ if !self.silence_errors {
+ report_autoderef_recursion_limit_error(tcx, self.span, self.cur_ty);
}
+ self.reached_recursion_limit = true;
return None;
}
}
impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
+ pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: ast::NodeId,
+ span: Span,
+ base_ty: Ty<'tcx>)
+ -> Autoderef<'a, 'gcx, 'tcx>
+ {
+ Autoderef {
+ infcx,
+ body_id,
+ param_env,
+ steps: vec![],
+ cur_ty: infcx.resolve_type_vars_if_possible(&base_ty),
+ obligations: vec![],
+ at_start: true,
+ include_raw_pointers: false,
+ silence_errors: false,
+ reached_recursion_limit: false,
+ span,
+ }
+ }
+
fn overloaded_deref_ty(&mut self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
debug!("overloaded_deref_ty({:?})", ty);
- let tcx = self.fcx.tcx();
+ let tcx = self.infcx.tcx;
// <cur_ty as Deref>
let trait_ref = TraitRef {
substs: tcx.mk_substs_trait(self.cur_ty, &[]),
};
- let cause = traits::ObligationCause::misc(self.span, self.fcx.body_id);
+ let cause = traits::ObligationCause::misc(self.span, self.body_id);
let obligation = traits::Obligation::new(cause.clone(),
- self.fcx.param_env,
+ self.param_env,
trait_ref.to_predicate());
- if !self.fcx.predicate_may_hold(&obligation) {
+ if !self.infcx.predicate_may_hold(&obligation) {
debug!("overloaded_deref_ty: cannot match obligation");
return None;
}
- let mut selcx = traits::SelectionContext::new(self.fcx);
- let normalized_ty = traits::normalize_projection_type(&mut selcx,
- self.fcx.param_env,
- ty::ProjectionTy::from_ref_and_name(
- tcx,
- trait_ref,
- Ident::from_str("Target"),
- ),
- cause,
- 0,
- &mut self.obligations);
-
- debug!("overloaded_deref_ty({:?}) = {:?}", ty, normalized_ty);
+ let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot();
+ let normalized_ty = fulfillcx.normalize_projection_type(
+ &self.infcx,
+ self.param_env,
+ ty::ProjectionTy::from_ref_and_name(
+ tcx,
+ trait_ref,
+ Ident::from_str("Target"),
+ ),
+ cause);
+ if let Err(e) = fulfillcx.select_where_possible(&self.infcx) {
+ // This shouldn't happen, except for evaluate/fulfill mismatches,
+ // but that's not a reason for an ICE (`predicate_may_hold` is conservative
+ // by design).
+ debug!("overloaded_deref_ty: encountered errors {:?} while fulfilling",
+ e);
+ return None;
+ }
+ let obligations = fulfillcx.pending_obligations();
+ debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})",
+ ty, normalized_ty, obligations);
+ self.obligations.extend(obligations);
- Some(self.fcx.resolve_type_vars_if_possible(&normalized_ty))
+ Some(self.infcx.resolve_type_vars_if_possible(&normalized_ty))
}
/// Returns the final type, generating an error if it is an
/// unresolved inference variable.
- pub fn unambiguous_final_ty(&self) -> Ty<'tcx> {
- self.fcx.structurally_resolved_type(self.span, self.cur_ty)
+ pub fn unambiguous_final_ty(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> {
+ fcx.structurally_resolved_type(self.span, self.cur_ty)
}
/// Returns the final type we ended up with, which may well be an
/// inference variable (we will resolve it first, if possible).
pub fn maybe_ambiguous_final_ty(&self) -> Ty<'tcx> {
- self.fcx.resolve_type_vars_if_possible(&self.cur_ty)
+ self.infcx.resolve_type_vars_if_possible(&self.cur_ty)
}
pub fn step_count(&self) -> usize {
}
/// Returns the adjustment steps.
- pub fn adjust_steps(&self, needs: Needs)
+ pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, needs: Needs)
-> Vec<Adjustment<'tcx>> {
- self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(needs))
+ fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs))
}
- pub fn adjust_steps_as_infer_ok(&self, needs: Needs)
+ pub fn adjust_steps_as_infer_ok(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, needs: Needs)
-> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
let mut obligations = vec![];
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
.chain(iter::once(self.cur_ty));
let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| {
if let AutoderefKind::Overloaded = kind {
- self.fcx.try_overloaded_deref(self.span, source, needs)
+ fcx.try_overloaded_deref(self.span, source, needs)
.and_then(|InferOk { value: method, obligations: o }| {
obligations.extend(o);
if let ty::Ref(region, _, mutbl) = method.sig.output().sty {
self
}
- pub fn finalize(self) {
- let fcx = self.fcx;
+ pub fn silence_errors(mut self) -> Self {
+ self.silence_errors = true;
+ self
+ }
+
+ pub fn reached_recursion_limit(&self) -> bool {
+ self.reached_recursion_limit
+ }
+
+ pub fn finalize(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) {
fcx.register_predicates(self.into_obligations());
}
}
}
+pub fn report_autoderef_recursion_limit_error<'a, 'gcx, 'tcx>(
+ tcx: TyCtxt<'a, 'gcx, 'tcx>, span: Span, ty: Ty<'tcx>)
+{
+ // We've reached the recursion limit, error gracefully.
+ let suggested_limit = *tcx.sess.recursion_limit.get() * 2;
+ let msg = format!("reached the recursion limit while auto-dereferencing `{:?}`",
+ ty);
+ let error_id = (DiagnosticMessageId::ErrorId(55), Some(span), msg);
+ let fresh = tcx.sess.one_time_diagnostics.borrow_mut().insert(error_id);
+ if fresh {
+ struct_span_err!(tcx.sess,
+ span,
+ E0055,
+ "reached the recursion limit while auto-dereferencing `{:?}`",
+ ty)
+ .span_label(span, "deref recursion limit reached")
+ .help(&format!(
+ "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate",
+ suggested_limit))
+ .emit();
+ }
+}
+
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'gcx, 'tcx> {
- Autoderef {
- fcx: self,
- steps: vec![],
- cur_ty: self.resolve_type_vars_if_possible(&base_ty),
- obligations: vec![],
- at_start: true,
- include_raw_pointers: false,
- span,
- }
+ Autoderef::new(self, self.param_env, self.body_id, span, base_ty)
}
pub fn try_overloaded_deref(&self,
while result.is_none() && autoderef.next().is_some() {
result = self.try_overloaded_call_step(call_expr, callee_expr, &autoderef);
}
- autoderef.finalize();
+ autoderef.finalize(self);
let output = match result {
None => {
callee_expr: &'gcx hir::Expr,
autoderef: &Autoderef<'a, 'gcx, 'tcx>)
-> Option<CallStep<'tcx>> {
- let adjusted_ty = autoderef.unambiguous_final_ty();
+ let adjusted_ty = autoderef.unambiguous_final_ty(self);
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
call_expr,
adjusted_ty);
// If the callee is a bare function or a closure, then we're all set.
match adjusted_ty.sty {
ty::FnDef(..) | ty::FnPtr(_) => {
- let adjustments = autoderef.adjust_steps(Needs::None);
+ let adjustments = autoderef.adjust_steps(self, Needs::None);
self.apply_adjustments(callee_expr, adjustments);
return Some(CallStep::Builtin(adjusted_ty));
}
infer::FnCall,
&closure_ty
).0;
- let adjustments = autoderef.adjust_steps(Needs::None);
+ let adjustments = autoderef.adjust_steps(self, Needs::None);
self.record_deferred_call_resolution(def_id, DeferredCallResolution {
call_expr,
callee_expr,
}
self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
- let mut adjustments = autoderef.adjust_steps(Needs::None);
+ let mut adjustments = autoderef.adjust_steps(self, Needs::None);
adjustments.extend(autoref);
self.apply_adjustments(callee_expr, adjustments);
CallStep::Overloaded(method)
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::traits::Obligation;
use rustc::traits::error_reporting::ArgKind;
-use rustc::ty::{self, ToPolyTraitRef, Ty, GenericParamDefKind};
+use rustc::ty::{self, Ty, GenericParamDefKind};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::Substs;
use std::cmp;
&self,
expected_vid: ty::TyVid,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
- let fulfillment_cx = self.fulfillment_cx.borrow();
- // Here `expected_ty` is known to be a type inference variable.
-
- let expected_sig = fulfillment_cx
- .pending_obligations()
- .iter()
- .filter_map(|obligation| {
+ let expected_sig = self.obligations_for_self_ty(expected_vid)
+ .find_map(|(_, obligation)| {
debug!(
"deduce_expectations_from_obligations: obligation.predicate={:?}",
obligation.predicate
if let ty::Predicate::Projection(ref proj_predicate) = obligation.predicate {
// Given a Projection predicate, we can potentially infer
// the complete signature.
- let trait_ref = proj_predicate.to_poly_trait_ref(self.tcx);
- self.self_type_matches_expected_vid(trait_ref, expected_vid)
- .and_then(|_| {
- self.deduce_sig_from_projection(
- Some(obligation.cause.span),
- proj_predicate
- )
- })
+ self.deduce_sig_from_projection(
+ Some(obligation.cause.span),
+ proj_predicate
+ )
} else {
None
}
- })
- .next();
+ });
// Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur if there is a trait-reference
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
- let expected_kind = fulfillment_cx
- .pending_obligations()
- .iter()
- .filter_map(|obligation| {
- let opt_trait_ref = match obligation.predicate {
- ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref(self.tcx)),
- ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
- ty::Predicate::Subtype(..) => None,
- ty::Predicate::RegionOutlives(..) => None,
- ty::Predicate::TypeOutlives(..) => None,
- ty::Predicate::WellFormed(..) => None,
- ty::Predicate::ObjectSafe(..) => None,
- ty::Predicate::ConstEvaluatable(..) => None,
-
- // N.B., this predicate is created by breaking down a
- // `ClosureType: FnFoo()` predicate, where
- // `ClosureType` represents some `Closure`. It can't
- // possibly be referring to the current closure,
- // because we haven't produced the `Closure` for
- // this closure yet; this is exactly why the other
- // code is looking for a self type of a unresolved
- // inference variable.
- ty::Predicate::ClosureKind(..) => None,
- };
- opt_trait_ref
- .and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
- .and_then(|tr| self.tcx.lang_items().fn_trait_kind(tr.def_id()))
- })
+ let expected_kind = self.obligations_for_self_ty(expected_vid)
+ .filter_map(|(tr, _)| self.tcx.lang_items().fn_trait_kind(tr.def_id()))
.fold(None, |best, cur| {
Some(best.map_or(cur, |best| cmp::min(best, cur)))
});
Some(ExpectedSig { cause_span, sig })
}
- fn self_type_matches_expected_vid(
- &self,
- trait_ref: ty::PolyTraitRef<'tcx>,
- expected_vid: ty::TyVid,
- ) -> Option<ty::PolyTraitRef<'tcx>> {
- let self_ty = self.shallow_resolve(trait_ref.self_ty());
- debug!(
- "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
- trait_ref, self_ty
- );
- match self_ty.sty {
- ty::Infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
- _ => None,
- }
- }
-
fn sig_of_closure(
&self,
expr_def_id: DefId,
let needs = Needs::maybe_mut_place(mt_b.mutbl);
let InferOk { value: mut adjustments, obligations: o }
- = autoderef.adjust_steps_as_infer_ok(needs);
+ = autoderef.adjust_steps_as_infer_ok(self, needs);
obligations.extend(o);
obligations.extend(autoderef.into_obligations());
};
match selcx.select(&obligation.with(trait_ref)) {
// Uncertain or unimplemented.
- Ok(None) |
+ Ok(None) => {
+ if trait_ref.def_id() == unsize_did {
+ let trait_ref = self.resolve_type_vars_if_possible(&trait_ref);
+ let self_ty = trait_ref.skip_binder().self_ty();
+ let unsize_ty = trait_ref.skip_binder().input_types().nth(1).unwrap();
+ debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_ref);
+ match (&self_ty.sty, &unsize_ty.sty) {
+ (ty::Infer(ty::TyVar(v)),
+ ty::Dynamic(..)) if self.type_var_is_sized(*v) => {
+ debug!("coerce_unsized: have sized infer {:?}", v);
+ coercion.obligations.push(obligation);
+ // `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
+ // for unsizing.
+ }
+ _ => {
+ // Some other case for `$0: Unsize<Something>`. Note that we
+ // hit this case even if `Something` is a sized type, so just
+ // don't do the coercion.
+ debug!("coerce_unsized: ambiguous unsize");
+ return Err(TypeError::Mismatch);
+ }
+ }
+ } else {
+ debug!("coerce_unsized: early return - ambiguous");
+ return Err(TypeError::Mismatch);
+ }
+ }
Err(traits::Unimplemented) => {
debug!("coerce_unsized: early return - can't prove obligation");
return Err(TypeError::Mismatch);
let (_, n) = autoderef.nth(pick.autoderefs).unwrap();
assert_eq!(n, pick.autoderefs);
- let mut adjustments = autoderef.adjust_steps(Needs::None);
+ let mut adjustments = autoderef.adjust_steps(self, Needs::None);
- let mut target = autoderef.unambiguous_final_ty();
+ let mut target = autoderef.unambiguous_final_ty(self);
if let Some(mutbl) = pick.autoref {
let region = self.next_region_var(infer::Autoref(self.span));
assert!(pick.unsize.is_none());
}
- autoderef.finalize();
+ autoderef.finalize(self);
// Write out the final adjustments.
self.apply_adjustments(self.self_expr, adjustments);
pub fn provide(providers: &mut ty::query::Providers) {
suggest::provide(providers);
+ probe::provide(providers);
}
#[derive(Clone, Copy, Debug)]
use super::{CandidateSource, ImplSource, TraitSource};
use super::suggest;
+use check::autoderef::{self, Autoderef};
use check::FnCtxt;
use hir::def_id::DefId;
use hir::def::Def;
use namespace::Namespace;
+
+use rustc_data_structures::sync::Lrc;
use rustc::hir;
use rustc::lint;
use rustc::session::config::nightly_options;
use rustc::ty::subst::{Subst, Substs};
use rustc::traits::{self, ObligationCause};
-use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
+use rustc::traits::query::{CanonicalTyGoal};
+use rustc::traits::query::method_autoderef::{CandidateStep, MethodAutoderefStepsResult};
+use rustc::traits::query::method_autoderef::{MethodAutoderefBadTy};
+use rustc::ty::{self, ParamEnvAnd, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TraitRef, TypeFoldable};
use rustc::ty::GenericParamDefKind;
use rustc::infer::type_variable::TypeVariableOrigin;
use rustc::util::nodemap::FxHashSet;
use rustc::infer::{self, InferOk};
+use rustc::infer::canonical::{Canonical, QueryResponse};
+use rustc::infer::canonical::{OriginalQueryValues};
use rustc::middle::stability;
use syntax::ast;
use syntax::util::lev_distance::{lev_distance, find_best_match_for_name};
-use syntax_pos::{Span, symbol::Symbol};
+use syntax_pos::{DUMMY_SP, Span, symbol::Symbol};
use std::iter;
use std::mem;
use std::ops::Deref;
-use std::rc::Rc;
use std::cmp::max;
use self::CandidateKind::*;
mode: Mode,
method_name: Option<ast::Ident>,
return_type: Option<Ty<'tcx>>,
- steps: Rc<Vec<CandidateStep<'tcx>>>,
+
+ /// This is the OriginalQueryValues for the steps queries
+ /// that are answered in steps.
+ orig_steps_var_values: OriginalQueryValues<'tcx>,
+ steps: Lrc<Vec<CandidateStep<'gcx>>>,
+
inherent_candidates: Vec<Candidate<'tcx>>,
extension_candidates: Vec<Candidate<'tcx>>,
impl_dups: FxHashSet<DefId>,
}
}
-#[derive(Debug)]
-struct CandidateStep<'tcx> {
- self_ty: Ty<'tcx>,
- autoderefs: usize,
- // true if the type results from a dereference of a raw pointer.
- // when assembling candidates, we include these steps, but not when
- // picking methods. This so that if we have `foo: *const Foo` and `Foo` has methods
- // `fn by_raw_ptr(self: *const Self)` and `fn by_ref(&self)`, then
- // `foo.by_raw_ptr()` will work and `foo.by_ref()` won't.
- from_unsafe_deref: bool,
- unsize: bool,
-}
-
#[derive(Debug)]
struct Candidate<'tcx> {
xform_self_ty: Ty<'tcx>,
-> Result<R, MethodError<'tcx>>
where OP: FnOnce(ProbeContext<'a, 'gcx, 'tcx>) -> Result<R, MethodError<'tcx>>
{
- // FIXME(#18741) -- right now, creating the steps involves evaluating the
- // `*` operator, which registers obligations that then escape into
- // the global fulfillment context and thus has global
- // side-effects. This is a bit of a pain to refactor. So just let
- // it ride, although it's really not great, and in fact could I
- // think cause spurious errors. Really though this part should
- // take place in the `self.probe` below.
+ let mut orig_values = OriginalQueryValues::default();
+ let param_env_and_self_ty =
+ self.infcx.canonicalize_query(
+ &ParamEnvAnd {
+ param_env: self.param_env,
+ value: self_ty
+ }, &mut orig_values);
+
let steps = if mode == Mode::MethodCall {
- match self.create_steps(span, scope_expr_id, self_ty, is_suggestion) {
- Some(steps) => steps,
- None => {
- return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
- Vec::new(),
- Vec::new(),
- None,
- mode)))
- }
- }
+ self.tcx.method_autoderef_steps(param_env_and_self_ty)
} else {
- vec![CandidateStep {
- self_ty,
- autoderefs: 0,
- from_unsafe_deref: false,
- unsize: false,
- }]
+ self.infcx.probe(|_| {
+ // Mode::Path - the deref steps is "trivial". This turns
+ // our CanonicalQuery into a "trivial" QueryResponse. This
+ // is a bit inefficient, but I don't think that writing
+ // special handling for this "trivial case" is a good idea.
+
+ let infcx = &self.infcx;
+ let (ParamEnvAnd {
+ param_env: _,
+ value: self_ty
+ }, canonical_inference_vars) =
+ infcx.instantiate_canonical_with_fresh_inference_vars(
+ span, ¶m_env_and_self_ty);
+ debug!("probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}",
+ param_env_and_self_ty, self_ty);
+ MethodAutoderefStepsResult {
+ steps: Lrc::new(vec![CandidateStep {
+ self_ty: self.make_query_response_ignoring_pending_obligations(
+ canonical_inference_vars, self_ty),
+ autoderefs: 0,
+ from_unsafe_deref: false,
+ unsize: false,
+ }]),
+ opt_bad_ty: None,
+ reached_recursion_limit: false
+ }
+ })
};
+ // If our autoderef loop had reached the recursion limit,
+ // report an overflow error, but continue going on with
+ // the truncated autoderef list.
+ if steps.reached_recursion_limit {
+ self.probe(|_| {
+ let ty = &steps.steps.last().unwrap_or_else(|| {
+ span_bug!(span, "reached the recursion limit in 0 steps?")
+ }).self_ty;
+ let ty = self.probe_instantiate_query_response(span, &orig_values, ty)
+ .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
+ autoderef::report_autoderef_recursion_limit_error(self.tcx, span,
+ ty.value);
+ });
+ }
+
+
+ // If we encountered an `_` type or an error type during autoderef, this is
+ // ambiguous.
+ if let Some(bad_ty) = &steps.opt_bad_ty {
+ if is_suggestion.0 {
+ // Ambiguity was encountered during a suggestion. Just keep going.
+ debug!("ProbeContext: encountered ambiguity in suggestion");
+ } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types {
+ // this case used to be allowed by the compiler,
+ // so we do a future-compat lint here for the 2015 edition
+ // (see https://github.com/rust-lang/rust/issues/46906)
+ if self.tcx.sess.rust_2018() {
+ span_err!(self.tcx.sess, span, E0699,
+ "the type of this value must be known \
+ to call a method on a raw pointer on it");
+ } else {
+ self.tcx.lint_node(
+ lint::builtin::TYVAR_BEHIND_RAW_POINTER,
+ scope_expr_id,
+ span,
+ "type annotations needed");
+ }
+ } else {
+ // Encountered a real ambiguity, so abort the lookup. If `ty` is not
+ // an `Err`, report the right "type annotations needed" error pointing
+ // to it.
+ let ty = &bad_ty.ty;
+ let ty = self.probe_instantiate_query_response(span, &orig_values, ty)
+ .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
+ let ty = self.structurally_resolved_type(span, ty.value);
+ assert_eq!(ty, self.tcx.types.err);
+ return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ None,
+ mode)));
+ }
+ }
+
debug!("ProbeContext: steps for self_ty={:?} are {:?}",
self_ty,
steps);
+
// this creates one big transaction so that all type variables etc
// that we create during the probe process are removed later
self.probe(|_| {
let mut probe_cx = ProbeContext::new(
- self, span, mode, method_name, return_type, Rc::new(steps), is_suggestion,
+ self, span, mode, method_name, return_type, orig_values,
+ steps.steps, is_suggestion,
);
probe_cx.assemble_inherent_candidates();
op(probe_cx)
})
}
+}
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ providers.method_autoderef_steps = method_autoderef_steps;
+}
+
+fn method_autoderef_steps<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>,
+ goal: CanonicalTyGoal<'tcx>)
+ -> MethodAutoderefStepsResult<'gcx>
+{
+ debug!("method_autoderef_steps({:?})", goal);
- fn create_steps(&self,
- span: Span,
- scope_expr_id: ast::NodeId,
- self_ty: Ty<'tcx>,
- is_suggestion: IsSuggestion)
- -> Option<Vec<CandidateStep<'tcx>>> {
- // FIXME: we don't need to create the entire steps in one pass
+ tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| {
+ let ParamEnvAnd { param_env, value: self_ty } = goal;
- let mut autoderef = self.autoderef(span, self_ty).include_raw_pointers();
+ let mut autoderef = Autoderef::new(infcx, param_env, ast::DUMMY_NODE_ID, DUMMY_SP, self_ty)
+ .include_raw_pointers()
+ .silence_errors();
let mut reached_raw_pointer = false;
let mut steps: Vec<_> = autoderef.by_ref()
.map(|(ty, d)| {
let step = CandidateStep {
- self_ty: ty,
+ self_ty: infcx.make_query_response_ignoring_pending_obligations(
+ inference_vars.clone(), ty),
autoderefs: d,
from_unsafe_deref: reached_raw_pointer,
unsize: false,
.collect();
let final_ty = autoderef.maybe_ambiguous_final_ty();
- match final_ty.sty {
- ty::Infer(ty::TyVar(_)) => {
- // Ended in an inference variable. If we are doing
- // a real method lookup, this is a hard error because it's
- // possible that there will be multiple applicable methods.
- if !is_suggestion.0 {
- if reached_raw_pointer
- && !self.tcx.features().arbitrary_self_types {
- // this case used to be allowed by the compiler,
- // so we do a future-compat lint here for the 2015 edition
- // (see https://github.com/rust-lang/rust/issues/46906)
- if self.tcx.sess.rust_2018() {
- span_err!(self.tcx.sess, span, E0699,
- "the type of this value must be known \
- to call a method on a raw pointer on it");
- } else {
- self.tcx.lint_node(
- lint::builtin::TYVAR_BEHIND_RAW_POINTER,
- scope_expr_id,
- span,
- "type annotations needed");
- }
- } else {
- let t = self.structurally_resolved_type(span, final_ty);
- assert_eq!(t, self.tcx.types.err);
- return None
- }
- } else {
- // If we're just looking for suggestions,
- // though, ambiguity is no big thing, we can
- // just ignore it.
- }
+ let opt_bad_ty = match final_ty.sty {
+ ty::Infer(ty::TyVar(_)) |
+ ty::Error => {
+ Some(MethodAutoderefBadTy {
+ reached_raw_pointer,
+ ty: infcx.make_query_response_ignoring_pending_obligations(
+ inference_vars, final_ty)
+ })
}
ty::Array(elem_ty, _) => {
let dereferences = steps.len() - 1;
steps.push(CandidateStep {
- self_ty: self.tcx.mk_slice(elem_ty),
+ self_ty: infcx.make_query_response_ignoring_pending_obligations(
+ inference_vars, infcx.tcx.mk_slice(elem_ty)),
autoderefs: dereferences,
// this could be from an unsafe deref if we had
// a *mut/const [T; N]
from_unsafe_deref: reached_raw_pointer,
unsize: true,
});
+
+ None
}
- ty::Error => return None,
- _ => (),
- }
+ _ => None
+ };
- debug!("create_steps: steps={:?}", steps);
+ debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
- Some(steps)
- }
+ MethodAutoderefStepsResult {
+ steps: Lrc::new(steps),
+ opt_bad_ty: opt_bad_ty.map(Lrc::new),
+ reached_recursion_limit: autoderef.reached_recursion_limit()
+ }
+ })
}
+
impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
fn new(fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
span: Span,
mode: Mode,
method_name: Option<ast::Ident>,
return_type: Option<Ty<'tcx>>,
- steps: Rc<Vec<CandidateStep<'tcx>>>,
+ orig_steps_var_values: OriginalQueryValues<'tcx>,
+ steps: Lrc<Vec<CandidateStep<'gcx>>>,
is_suggestion: IsSuggestion)
-> ProbeContext<'a, 'gcx, 'tcx> {
ProbeContext {
inherent_candidates: Vec::new(),
extension_candidates: Vec::new(),
impl_dups: FxHashSet::default(),
- steps: steps,
+ orig_steps_var_values,
+ steps,
static_candidates: Vec::new(),
allow_similar_names: false,
private_candidate: None,
fn assemble_inherent_candidates(&mut self) {
let steps = self.steps.clone();
for step in steps.iter() {
- self.assemble_probe(step.self_ty);
+ self.assemble_probe(&step.self_ty);
}
}
- fn assemble_probe(&mut self, self_ty: Ty<'tcx>) {
+ fn assemble_probe(&mut self, self_ty: &Canonical<'gcx, QueryResponse<'gcx, Ty<'gcx>>>) {
debug!("assemble_probe: self_ty={:?}", self_ty);
let lang_items = self.tcx.lang_items();
- match self_ty.sty {
+ match self_ty.value.value.sty {
ty::Dynamic(ref data, ..) => {
let p = data.principal();
- self.assemble_inherent_candidates_from_object(self_ty, p);
+ self.fcx.probe(|_| {
+ let InferOk { value: self_ty, obligations: _ } =
+ self.fcx.probe_instantiate_query_response(
+ self.span, &self.orig_steps_var_values, self_ty)
+ .unwrap_or_else(|_| {
+ span_bug!(self.span, "{:?} was applicable but now isn't?", self_ty)
+ });
+ self.assemble_inherent_candidates_from_object(self_ty);
+ });
self.assemble_inherent_impl_candidates_for_type(p.def_id());
}
ty::Adt(def, _) => {
self.assemble_inherent_impl_candidates_for_type(did);
}
ty::Param(p) => {
- self.assemble_inherent_candidates_from_param(self_ty, p);
+ self.assemble_inherent_candidates_from_param(p);
}
ty::Char => {
let lang_def_id = lang_items.char_impl();
}
fn assemble_inherent_candidates_from_object(&mut self,
- self_ty: Ty<'tcx>,
- principal: ty::PolyExistentialTraitRef<'tcx>) {
+ self_ty: Ty<'tcx>) {
debug!("assemble_inherent_candidates_from_object(self_ty={:?})",
self_ty);
+ let principal = match self_ty.sty {
+ ty::Dynamic(ref data, ..) => data.principal(),
+ _ => span_bug!(self.span, "non-object {:?} in assemble_inherent_candidates_from_object",
+ self_ty)
+ };
+
// It is illegal to invoke a method on a trait instance that
// refers to the `Self` type. An error will be reported by
// `enforce_object_limitations()` if the method refers to the
}
fn assemble_inherent_candidates_from_param(&mut self,
- _rcvr_ty: Ty<'tcx>,
param_ty: ty::ParamTy) {
// FIXME -- Do we want to commit to this behavior for param bounds?
// a raw pointer
!step.self_ty.references_error() && !step.from_unsafe_deref
}).flat_map(|step| {
- self.pick_by_value_method(step).or_else(|| {
- self.pick_autorefd_method(step, hir::MutImmutable).or_else(|| {
- self.pick_autorefd_method(step, hir::MutMutable)
+ let InferOk { value: self_ty, obligations: _ } =
+ self.fcx.probe_instantiate_query_response(
+ self.span, &self.orig_steps_var_values, &step.self_ty
+ ).unwrap_or_else(|_| {
+ span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty)
+ });
+ self.pick_by_value_method(step, self_ty).or_else(|| {
+ self.pick_autorefd_method(step, self_ty, hir::MutImmutable).or_else(|| {
+ self.pick_autorefd_method(step, self_ty, hir::MutMutable)
})})})
.next()
}
- fn pick_by_value_method(&mut self, step: &CandidateStep<'tcx>) -> Option<PickResult<'tcx>> {
+ fn pick_by_value_method(&mut self, step: &CandidateStep<'gcx>, self_ty: Ty<'tcx>)
+ -> Option<PickResult<'tcx>>
+ {
//! For each type `T` in the step list, this attempts to find a
//! method where the (transformed) self type is exactly `T`. We
//! do however do one transformation on the adjustment: if we
return None;
}
- self.pick_method(step.self_ty).map(|r| {
+ self.pick_method(self_ty).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
// Insert a `&*` or `&mut *` if this is a reference type:
- if let ty::Ref(_, _, mutbl) = step.self_ty.sty {
+ if let ty::Ref(_, _, mutbl) = step.self_ty.value.value.sty {
pick.autoderefs += 1;
pick.autoref = Some(mutbl);
}
})
}
- fn pick_autorefd_method(&mut self, step: &CandidateStep<'tcx>, mutbl: hir::Mutability)
+ fn pick_autorefd_method(&mut self,
+ step: &CandidateStep<'gcx>,
+ self_ty: Ty<'tcx>,
+ mutbl: hir::Mutability)
-> Option<PickResult<'tcx>> {
let tcx = self.tcx;
let autoref_ty = tcx.mk_ref(region,
ty::TypeAndMut {
- ty: step.self_ty, mutbl
+ ty: self_ty, mutbl
});
self.pick_method(autoref_ty).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
pick.autoref = Some(mutbl);
pick.unsize = if step.unsize {
- Some(step.self_ty)
+ Some(self_ty)
} else {
None
};
let steps = self.steps.clone();
self.probe(|_| {
let mut pcx = ProbeContext::new(self.fcx, self.span, self.mode, self.method_name,
- self.return_type, steps, IsSuggestion(true));
+ self.return_type,
+ self.orig_steps_var_values.clone(),
+ steps, IsSuggestion(true));
pcx.allow_similar_names = true;
pcx.assemble_inherent_candidates();
pcx.assemble_extension_candidates_for_traits_in_scope(ast::DUMMY_NODE_ID)?;
use rustc::hir::itemlikevisit::ItemLikeVisitor;
use middle::lang_items;
use namespace::Namespace;
+use rustc::infer::{self, InferCtxt, InferOk, InferResult, RegionVariableOrigin};
+use rustc::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::sync::Lrc;
use rustc_target::spec::abi::Abi;
-use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
use rustc::infer::opaque_types::OpaqueTypeDecl;
use rustc::infer::type_variable::{TypeVariableOrigin};
use rustc::middle::region;
use rustc::ty::subst::{CanonicalUserSubsts, UnpackedKind, Subst, Substs,
UserSelfTy, UserSubsts};
use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
-use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, Visibility, ToPredicate,
- RegionKind};
+use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, RegionKind, Visibility,
+ ToPolyTraitRef, ToPredicate};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::query::Providers;
use session::{CompileIncomplete, config, Session};
use TypeAndSubsts;
use lint;
+use util::captures::Captures;
use util::common::{ErrorReported, indenter};
use util::nodemap::{DefIdMap, DefIdSet, FxHashMap, FxHashSet, NodeMap};
while result.is_none() && autoderef.next().is_some() {
result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
}
- autoderef.finalize();
+ autoderef.finalize(self);
result
}
index_ty: Ty<'tcx>)
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
{
- let adjusted_ty = autoderef.unambiguous_final_ty();
+ let adjusted_ty = autoderef.unambiguous_final_ty(self);
debug!("try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
index_ty={:?})",
expr,
debug!("try_index_step: success, using overloaded indexing");
let method = self.register_infer_ok_obligations(ok);
- let mut adjustments = autoderef.adjust_steps(needs);
+ let mut adjustments = autoderef.adjust_steps(self, needs);
if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].sty {
let mutbl = match r_mutbl {
hir::MutImmutable => AutoBorrowMutability::Immutable,
method.sig.output()
}
+ fn self_type_matches_expected_vid(
+ &self,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ expected_vid: ty::TyVid,
+ ) -> bool {
+ let self_ty = self.shallow_resolve(trait_ref.self_ty());
+ debug!(
+ "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})",
+ trait_ref, self_ty, expected_vid
+ );
+ match self_ty.sty {
+ ty::Infer(ty::TyVar(found_vid)) => {
+ // FIXME: consider using `sub_root_var` here so we
+ // can see through subtyping.
+ let found_vid = self.root_var(found_vid);
+ debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
+ expected_vid == found_vid
+ }
+ _ => false
+ }
+ }
+
+ fn obligations_for_self_ty<'b>(&'b self, self_ty: ty::TyVid)
+ -> impl Iterator<Item=(ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)>
+ + Captures<'gcx> + 'b
+ {
+ // FIXME: consider using `sub_root_var` here so we
+ // can see through subtyping.
+ let ty_var_root = self.root_var(self_ty);
+ debug!("obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}",
+ self_ty, ty_var_root,
+ self.fulfillment_cx.borrow().pending_obligations());
+
+ self.fulfillment_cx
+ .borrow()
+ .pending_obligations()
+ .into_iter()
+ .filter_map(move |obligation| match obligation.predicate {
+ ty::Predicate::Projection(ref data) =>
+ Some((data.to_poly_trait_ref(self.tcx), obligation)),
+ ty::Predicate::Trait(ref data) =>
+ Some((data.to_poly_trait_ref(), obligation)),
+ ty::Predicate::Subtype(..) => None,
+ ty::Predicate::RegionOutlives(..) => None,
+ ty::Predicate::TypeOutlives(..) => None,
+ ty::Predicate::WellFormed(..) => None,
+ ty::Predicate::ObjectSafe(..) => None,
+ ty::Predicate::ConstEvaluatable(..) => None,
+ // N.B., this predicate is created by breaking down a
+ // `ClosureType: FnFoo()` predicate, where
+ // `ClosureType` represents some `Closure`. It can't
+ // possibly be referring to the current closure,
+ // because we haven't produced the `Closure` for
+ // this closure yet; this is exactly why the other
+ // code is looking for a self type of a unresolved
+ // inference variable.
+ ty::Predicate::ClosureKind(..) => None,
+ }).filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root))
+ }
+
+ fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
+ self.obligations_for_self_ty(self_ty).any(|(tr, _)| {
+ Some(tr.def_id()) == self.tcx.lang_items().sized_trait()
+ })
+ }
+
/// Generic function that factors out common logic from function calls,
/// method calls and overloaded operators.
fn check_argument_types(&self,
// of error recovery.
self.write_field_index(expr.id, index);
if field.vis.is_accessible_from(def_scope, self.tcx) {
- let adjustments = autoderef.adjust_steps(needs);
+ let adjustments = autoderef.adjust_steps(self, needs);
self.apply_adjustments(base, adjustments);
- autoderef.finalize();
+ autoderef.finalize(self);
self.tcx.check_stability(field.did, Some(expr.id), expr.span);
return field_ty;
if let Ok(index) = fstr.parse::<usize>() {
if fstr == index.to_string() {
if let Some(field_ty) = tys.get(index) {
- let adjustments = autoderef.adjust_steps(needs);
+ let adjustments = autoderef.adjust_steps(self, needs);
self.apply_adjustments(base, adjustments);
- autoderef.finalize();
+ autoderef.finalize(self);
self.write_field_index(expr.id, index);
return field_ty;
_ => {}
}
}
- autoderef.unambiguous_final_ty();
+ autoderef.unambiguous_final_ty(self);
if let Some((did, field_ty)) = private_candidate {
let struct_path = self.tcx().item_path_str(did);
};
(ctxt, result)
}
+
+ /// Instantiate a QueryResponse in a probe context, without a
+ /// good ObligationCause.
+ fn probe_instantiate_query_response(
+ &self,
+ span: Span,
+ original_values: &OriginalQueryValues<'tcx>,
+ query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+ ) -> InferResult<'tcx, Ty<'tcx>>
+ {
+ self.instantiate_query_response_and_region_obligations(
+ &traits::ObligationCause::misc(span, self.body_id),
+ self.param_env,
+ original_values,
+ query_result)
+ }
}
pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
use hir::def_id::DefId;
use rustc::traits::{self, ObligationCauseCode};
-use rustc::ty::{self, Lift, Ty, TyCtxt, TyKind, GenericParamDefKind, TypeFoldable};
+use rustc::ty::{self, Lift, Ty, TyCtxt, TyKind, GenericParamDefKind, TypeFoldable, ToPredicate};
use rustc::ty::subst::{Subst, Substs};
-use rustc::ty::util::ExplicitSelf;
use rustc::util::nodemap::{FxHashSet, FxHashMap};
use rustc::middle::lang_items;
use rustc::infer::opaque_types::may_define_existential_type;
&ty::Binder::bind(self_ty)
);
- let self_arg_ty = sig.inputs()[0];
+ let receiver_ty = sig.inputs()[0];
- let cause = fcx.cause(span, ObligationCauseCode::MethodReceiver);
- let self_arg_ty = fcx.normalize_associated_types_in(span, &self_arg_ty);
- let self_arg_ty = fcx.tcx.liberate_late_bound_regions(
+ let receiver_ty = fcx.normalize_associated_types_in(span, &receiver_ty);
+ let receiver_ty = fcx.tcx.liberate_late_bound_regions(
method.def_id,
- &ty::Binder::bind(self_arg_ty)
+ &ty::Binder::bind(receiver_ty)
);
- let mut autoderef = fcx.autoderef(span, self_arg_ty).include_raw_pointers();
+ if fcx.tcx.features().arbitrary_self_types {
+ if !receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
+ // report error, arbitrary_self_types was enabled
+ fcx.tcx.sess.diagnostic().mut_span_err(
+ span, &format!("invalid method receiver type: {:?}", receiver_ty)
+ ).note("type of `self` must be `Self` or a type that dereferences to it")
+ .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
+ .code(DiagnosticId::Error("E0307".into()))
+ .emit();
+ }
+ } else {
+ if !receiver_is_valid(fcx, span, receiver_ty, self_ty, false) {
+ if receiver_is_valid(fcx, span, receiver_ty, self_ty, true) {
+ // report error, would have worked with arbitrary_self_types
+ feature_gate::feature_err(
+ &fcx.tcx.sess.parse_sess,
+ "arbitrary_self_types",
+ span,
+ GateIssue::Language,
+ &format!(
+ "`{}` cannot be used as the type of `self` without \
+ the `arbitrary_self_types` feature",
+ receiver_ty,
+ ),
+ ).help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
+ .emit();
+ } else {
+ // report error, would not have worked with arbitrary_self_types
+ fcx.tcx.sess.diagnostic().mut_span_err(
+ span, &format!("invalid method receiver type: {:?}", receiver_ty)
+ ).note("type must be `Self` or a type that dereferences to it")
+ .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
+ .code(DiagnosticId::Error("E0307".into()))
+ .emit();
+ }
+ }
+ }
+}
+
+/// returns true if `receiver_ty` would be considered a valid receiver type for `self_ty`. If
+/// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly
+/// through a `*const/mut T` raw pointer. If the feature is not enabled, the requirements are more
+/// strict: `receiver_ty` must implement `Receiver` and directly implement `Deref<Target=self_ty>`.
+///
+/// NB: there are cases this function returns `true` but causes an error to be emitted,
+/// particularly when `receiver_ty` derefs to a type that is the same as `self_ty` but has the
+/// wrong lifetime. Be careful of this if you are calling this function speculatively.
+fn receiver_is_valid<'fcx, 'tcx, 'gcx>(
+ fcx: &FnCtxt<'fcx, 'gcx, 'tcx>,
+ span: Span,
+ receiver_ty: Ty<'tcx>,
+ self_ty: Ty<'tcx>,
+ arbitrary_self_types_enabled: bool,
+) -> bool {
+ let cause = fcx.cause(span, traits::ObligationCauseCode::MethodReceiver);
+
+ let can_eq_self = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();
+
+ // `self: Self` is always valid
+ if can_eq_self(receiver_ty) {
+ if let Some(mut err) = fcx.demand_eqtype_with_origin(&cause, self_ty, receiver_ty) {
+ err.emit();
+ }
+ return true
+ }
+
+ let mut autoderef = fcx.autoderef(span, receiver_ty);
+
+ // the `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`
+ if arbitrary_self_types_enabled {
+ autoderef = autoderef.include_raw_pointers();
+ }
+
+ // the first type is `receiver_ty`, which we know its not equal to `self_ty`. skip it.
+ autoderef.next();
+ // keep dereferencing `receiver_ty` until we get to `self_ty`
loop {
if let Some((potential_self_ty, _)) = autoderef.next() {
- debug!("check_method_receiver: potential self type `{:?}` to match `{:?}`",
+ debug!("receiver_is_valid: potential self type `{:?}` to match `{:?}`",
potential_self_ty, self_ty);
- if fcx.infcx.can_eq(fcx.param_env, self_ty, potential_self_ty).is_ok() {
- autoderef.finalize();
+ if can_eq_self(potential_self_ty) {
+ autoderef.finalize(fcx);
+
if let Some(mut err) = fcx.demand_eqtype_with_origin(
- &cause, self_ty, potential_self_ty) {
+ &cause, self_ty, potential_self_ty
+ ) {
err.emit();
}
+
break
}
} else {
- fcx.tcx.sess.diagnostic().mut_span_err(
- span, &format!("invalid `self` type: {:?}", self_arg_ty))
- .note(&format!("type must be `{:?}` or a type that dereferences to it", self_ty))
- .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
- .code(DiagnosticId::Error("E0307".into()))
- .emit();
- return
+ debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`",
+ receiver_ty, self_ty);
+ return false
+ }
+
+ // without the `arbitrary_self_types` feature, `receiver_ty` must directly deref to
+ // `self_ty`. Enforce this by only doing one iteration of the loop
+ if !arbitrary_self_types_enabled {
+ return false
}
}
- let is_self_ty = |ty| fcx.infcx.can_eq(fcx.param_env, self_ty, ty).is_ok();
- let self_kind = ExplicitSelf::determine(self_arg_ty, is_self_ty);
+ // without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`
+ if !arbitrary_self_types_enabled {
+ let trait_def_id = match fcx.tcx.lang_items().receiver_trait() {
+ Some(did) => did,
+ None => {
+ debug!("receiver_is_valid: missing Receiver trait");
+ return false
+ }
+ };
- if !fcx.tcx.features().arbitrary_self_types {
- match self_kind {
- ExplicitSelf::ByValue |
- ExplicitSelf::ByReference(_, _) |
- ExplicitSelf::ByBox => (),
+ let trait_ref = ty::TraitRef{
+ def_id: trait_def_id,
+ substs: fcx.tcx.mk_substs_trait(receiver_ty, &[]),
+ };
- ExplicitSelf::ByRawPointer(_) => {
- feature_gate::feature_err(
- &fcx.tcx.sess.parse_sess,
- "arbitrary_self_types",
- span,
- GateIssue::Language,
- "raw pointer `self` is unstable")
- .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
- .emit();
- }
+ let obligation = traits::Obligation::new(
+ cause.clone(),
+ fcx.param_env,
+ trait_ref.to_predicate()
+ );
- ExplicitSelf::Other => {
- feature_gate::feature_err(
- &fcx.tcx.sess.parse_sess,
- "arbitrary_self_types",
- span,
- GateIssue::Language,"arbitrary `self` types are unstable")
- .help("consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`")
- .emit();
- }
+ if !fcx.predicate_must_hold(&obligation) {
+ debug!("receiver_is_valid: type `{:?}` does not implement `Receiver` trait",
+ receiver_ty);
+ return false
}
}
+
+ true
}
fn check_variances_for_type_defn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
extern crate syntax_pos;
extern crate arena;
+
#[macro_use] extern crate rustc;
extern crate rustc_platform_intrinsics as intrinsics;
extern crate rustc_data_structures;
fn build_external_function(cx: &DocContext, did: DefId) -> clean::Function {
let sig = cx.tcx.fn_sig(did);
- let constness = if cx.tcx.is_const_fn(did) {
+ let constness = if cx.tcx.is_min_const_fn(did) {
hir::Constness::Const
} else {
hir::Constness::NotConst
classes.push("unstable");
}
- if !s.deprecated_since.is_empty() {
+ if s.deprecation.is_some() {
classes.push("deprecated");
}
pub fn type_(&self) -> ItemType {
ItemType::from(self)
}
+
+ /// Returns the info in the item's `#[deprecated]` or `#[rustc_deprecated]` attributes.
+ ///
+ /// If the item is not deprecated, returns `None`.
+ pub fn deprecation(&self) -> Option<&Deprecation> {
+ self.deprecation
+ .as_ref()
+ .or_else(|| self.stability.as_ref().and_then(|s| s.deprecation.as_ref()))
+ }
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
(self.generics.clean(cx), (&self.decl, self.body).clean(cx))
});
+ let did = cx.tcx.hir().local_def_id(self.id);
+ let constness = if cx.tcx.is_min_const_fn(did) {
+ hir::Constness::Const
+ } else {
+ hir::Constness::NotConst
+ };
Item {
name: Some(self.name.clean(cx)),
attrs: self.attrs.clean(cx),
visibility: self.vis.clean(cx),
stability: self.stab.clean(cx),
deprecation: self.depr.clean(cx),
- def_id: cx.tcx.hir().local_def_id(self.id),
+ def_id: did,
inner: FunctionItem(Function {
decl,
generics,
- header: self.header,
+ header: hir::FnHeader { constness, ..self.header },
}),
}
}
ty::TraitContainer(_) => self.defaultness.has_value()
};
if provided {
- let constness = if cx.tcx.is_const_fn(self.def_id) {
+ let constness = if cx.tcx.is_min_const_fn(self.def_id) {
hir::Constness::Const
} else {
hir::Constness::NotConst
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Stability {
pub level: stability::StabilityLevel,
- pub feature: String,
+ pub feature: Option<String>,
pub since: String,
- pub deprecated_since: String,
- pub deprecated_reason: String,
- pub unstable_reason: String,
- pub issue: Option<u32>
+ pub deprecation: Option<Deprecation>,
+ pub unstable_reason: Option<String>,
+ pub issue: Option<u32>,
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Deprecation {
- pub since: String,
- pub note: String,
+ pub since: Option<String>,
+ pub note: Option<String>,
}
impl Clean<Stability> for attr::Stability {
fn clean(&self, _: &DocContext) -> Stability {
Stability {
level: stability::StabilityLevel::from_attr_level(&self.level),
- feature: self.feature.to_string(),
+ feature: Some(self.feature.to_string()).filter(|f| !f.is_empty()),
since: match self.level {
attr::Stable {ref since} => since.to_string(),
_ => String::new(),
},
- deprecated_since: match self.rustc_depr {
- Some(attr::RustcDeprecation {ref since, ..}) => since.to_string(),
- _=> String::new(),
- },
- deprecated_reason: match self.rustc_depr {
- Some(ref depr) => depr.reason.to_string(),
- _ => String::new(),
- },
+ deprecation: self.rustc_depr.as_ref().map(|d| {
+ Deprecation {
+ note: Some(d.reason.to_string()).filter(|r| !r.is_empty()),
+ since: Some(d.since.to_string()).filter(|d| !d.is_empty()),
+ }
+ }),
unstable_reason: match self.level {
- attr::Unstable { reason: Some(ref reason), .. } => reason.to_string(),
- _ => String::new(),
+ attr::Unstable { reason: Some(ref reason), .. } => Some(reason.to_string()),
+ _ => None,
},
issue: match self.level {
attr::Unstable {issue, ..} => Some(issue),
impl Clean<Deprecation> for attr::Deprecation {
fn clean(&self, _: &DocContext) -> Deprecation {
Deprecation {
- since: self.since.as_ref().map_or(String::new(), |s| s.to_string()),
- note: self.note.as_ref().map_or(String::new(), |s| s.to_string()),
+ since: self.since.map(|s| s.to_string()).filter(|s| !s.is_empty()),
+ note: self.note.map(|n| n.to_string()).filter(|n| !n.is_empty()),
}
}
}
glob_map: if resolver.make_glob_map { Some(resolver.glob_map.clone()) } else { None },
};
- let arenas = AllArenas::new();
+ let mut arenas = AllArenas::new();
let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
let output_filenames = driver::build_output_filenames(&input,
&None,
hir_map,
analysis,
resolutions,
- &arenas,
+ &mut arenas,
&name,
&output_filenames,
|tcx, analysis, _, result| {
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::flock;
-use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability};
+use clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, Mutability};
use config::RenderOptions;
use doctree;
use fold::DocFolder;
fn document_stability(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item,
is_hidden: bool) -> fmt::Result {
- let stabilities = short_stability(item, cx, true);
+ let stabilities = short_stability(item, cx);
if !stabilities.is_empty() {
write!(w, "<div class='stability{}'>", if is_hidden { " hidden" } else { "" })?;
for stability in stabilities {
_ => {
if myitem.name.is_none() { continue }
- let stabilities = short_stability(myitem, cx, false);
-
- let stab_docs = if !stabilities.is_empty() {
- stabilities.iter()
- .map(|s| format!("[{}]", s))
- .collect::<Vec<_>>()
- .as_slice()
- .join(" ")
- } else {
- String::new()
- };
-
let unsafety_flag = match myitem.inner {
clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func)
if func.header.unsafety == hir::Unsafety::Unsafe => {
<tr class='{stab}{add}module-item'>\
<td><a class=\"{class}\" href=\"{href}\" \
title='{title}'>{name}</a>{unsafety_flag}</td>\
- <td class='docblock-short'>{stab_docs}{docs}\
+ <td class='docblock-short'>{stab_tags}{docs}\
</td>\
</tr>",
name = *myitem.name.as_ref().unwrap(),
- stab_docs = stab_docs,
+ stab_tags = stability_tags(myitem),
docs = MarkdownSummaryLine(doc_value, &myitem.links()),
class = myitem.type_(),
add = add,
Ok(())
}
-fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<String> {
+/// Render the stability and deprecation tags that are displayed in the item's summary at the
+/// module level.
+fn stability_tags(item: &clean::Item) -> String {
+ let mut tags = String::new();
+
+ // The trailing space after each tag is to space it properly against the rest of the docs.
+ if item.deprecation().is_some() {
+ tags.push_str("[<div class='stab deprecated'>Deprecated</div>] ");
+ }
+
+ if let Some(stab) = item
+ .stability
+ .as_ref()
+ .filter(|s| s.level == stability::Unstable)
+ {
+ if stab.feature.as_ref().map(|s| &**s) == Some("rustc_private") {
+ tags.push_str("[<div class='stab internal'>Internal</div>] ");
+ } else {
+ tags.push_str("[<div class='stab unstable'>Experimental</div>] ");
+ }
+ }
+
+ if let Some(ref cfg) = item.attrs.cfg {
+ tags.push_str(&format!(
+ "[<div class='stab portability'>{}</div>] ",
+ cfg.render_short_html()
+ ));
+ }
+
+ tags
+}
+
+/// Render the stability and/or deprecation warning that is displayed at the top of the item's
+/// documentation.
+fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
let mut stability = vec![];
let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
- if let Some(stab) = item.stability.as_ref() {
- let deprecated_reason = if show_reason && !stab.deprecated_reason.is_empty() {
- format!(": {}", stab.deprecated_reason)
+ if let Some(Deprecation { since, note }) = &item.deprecation() {
+ let mut message = if let Some(since) = since {
+ if stability::deprecation_in_effect(since) {
+ format!("Deprecated since {}", Escape(since))
+ } else {
+ format!("Deprecating in {}", Escape(since))
+ }
} else {
- String::new()
+ String::from("Deprecated")
};
- if !stab.deprecated_since.is_empty() {
- let since = if show_reason {
- format!(" since {}", Escape(&stab.deprecated_since))
- } else {
- String::new()
- };
+
+ if let Some(note) = note {
let mut ids = cx.id_map.borrow_mut();
- let html = MarkdownHtml(&deprecated_reason, RefCell::new(&mut ids), error_codes);
- let text = if stability::deprecation_in_effect(&stab.deprecated_since) {
- format!("Deprecated{}{}", since, html)
+ let html = MarkdownHtml(¬e, RefCell::new(&mut ids), error_codes);
+ message.push_str(&format!(": {}", html));
+ }
+ stability.push(format!("<div class='stab deprecated'>{}</div>", message));
+ }
+
+ if let Some(stab) = item
+ .stability
+ .as_ref()
+ .filter(|stab| stab.level == stability::Unstable)
+ {
+ let is_rustc_private = stab.feature.as_ref().map(|s| &**s) == Some("rustc_private");
+
+ let mut message = if is_rustc_private {
+ "<span class='emoji'>⚙️</span> This is an internal compiler API."
+ } else {
+ "<span class='emoji'>🔬</span> This is a nightly-only experimental API."
+ }
+ .to_owned();
+
+ if let Some(feature) = stab.feature.as_ref() {
+ let mut feature = format!("<code>{}</code>", Escape(&feature));
+ if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) {
+ feature.push_str(&format!(
+ " <a href=\"{url}{issue}\">#{issue}</a>",
+ url = url,
+ issue = issue
+ ));
+ }
+
+ message.push_str(&format!(" ({})", feature));
+ }
+
+ if let Some(unstable_reason) = &stab.unstable_reason {
+ // Provide a more informative message than the compiler help.
+ let unstable_reason = if is_rustc_private {
+ "This crate is being loaded from the sysroot, a permanently unstable location \
+ for private compiler dependencies. It is not intended for general use. Prefer \
+ using a public version of this crate from \
+ [crates.io](https://crates.io) via [`Cargo.toml`]\
+ (https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)."
} else {
- format!("Deprecating in {}{}", Escape(&stab.deprecated_since), html)
+ unstable_reason
};
- stability.push(format!("<div class='stab deprecated'>{}</div>", text))
- };
- if stab.level == stability::Unstable {
- if show_reason {
- let unstable_extra = match (!stab.feature.is_empty(),
- &cx.shared.issue_tracker_base_url,
- stab.issue) {
- (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 =>
- format!(" (<code>{} </code><a href=\"{}{}\">#{}</a>)",
- Escape(&stab.feature), tracker_url, issue_no, issue_no),
- (false, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 =>
- format!(" (<a href=\"{}{}\">#{}</a>)", Escape(&tracker_url), issue_no,
- issue_no),
- (true, ..) =>
- format!(" (<code>{}</code>)", Escape(&stab.feature)),
- _ => String::new(),
- };
- if stab.unstable_reason.is_empty() {
- stability.push(format!("<div class='stab unstable'>\
- <span class=microscope>🔬</span> \
- This is a nightly-only experimental API. {}\
- </div>",
- unstable_extra));
- } else {
- let mut ids = cx.id_map.borrow_mut();
- let text = format!("<summary><span class=microscope>🔬</span> \
- This is a nightly-only experimental API. {}\
- </summary>{}",
- unstable_extra,
- MarkdownHtml(
- &stab.unstable_reason,
- RefCell::new(&mut ids),
- error_codes));
- stability.push(format!("<div class='stab unstable'><details>{}</details></div>",
- text));
- }
- } else {
- stability.push("<div class='stab unstable'>Experimental</div>".to_string())
- }
- };
- } else if let Some(depr) = item.deprecation.as_ref() {
- let note = if show_reason && !depr.note.is_empty() {
- format!(": {}", depr.note)
- } else {
- String::new()
- };
- let since = if show_reason && !depr.since.is_empty() {
- format!(" since {}", Escape(&depr.since))
- } else {
- String::new()
- };
+ let mut ids = cx.id_map.borrow_mut();
+ message = format!(
+ "<details><summary>{}</summary>{}</details>",
+ message,
+ MarkdownHtml(&unstable_reason, RefCell::new(&mut ids), error_codes)
+ );
+ }
- let mut ids = cx.id_map.borrow_mut();
- let text = if stability::deprecation_in_effect(&depr.since) {
- format!("Deprecated{}{}",
- since,
- MarkdownHtml(¬e, RefCell::new(&mut ids), error_codes))
+ let class = if is_rustc_private {
+ "internal"
} else {
- format!("Deprecating in {}{}",
- Escape(&depr.since),
- MarkdownHtml(¬e, RefCell::new(&mut ids), error_codes))
+ "unstable"
};
- stability.push(format!("<div class='stab deprecated'>{}</div>", text))
+ stability.push(format!("<div class='stab {}'>{}</div>", class, message));
}
if let Some(ref cfg) = item.attrs.cfg {
- stability.push(format!("<div class='stab portability'>{}</div>", if show_reason {
+ stability.push(format!(
+ "<div class='stab portability'>{}</div>",
cfg.render_long_html()
- } else {
- cfg.render_short_html()
- }));
+ ));
}
stability
let item_type = m.type_();
let id = cx.derive_id(format!("{}.{}", item_type, name));
let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
- write!(w, "{extra}<h3 id='{id}' class='method'>\
- <span id='{ns_id}' class='invisible'><code>",
+ write!(w, "{extra}<h3 id='{id}' class='method'><code id='{ns_id}'>",
extra = render_spotlight_traits(m)?,
id = id,
ns_id = ns_id)?;
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl)?;
write!(w, "</code>")?;
render_stability_since(w, m, t)?;
- write!(w, "</span></h3>")?;
+ write!(w, "</h3>")?;
document(w, cx, m)?;
Ok(())
}
Ok(())
}
-fn render_stability_since_raw<'a>(w: &mut fmt::Formatter,
- ver: Option<&'a str>,
- containing_ver: Option<&'a str>) -> fmt::Result {
+fn render_stability_since_raw<'a, T: fmt::Write>(
+ w: &mut T,
+ ver: Option<&'a str>,
+ containing_ver: Option<&'a str>,
+) -> fmt::Result {
if let Some(v) = ver {
if containing_ver != ver && v.len() > 0 {
- write!(w, "<div class='since' title='Stable since Rust version {0}'>{0}</div>",
- v)?
+ write!(w, "<div class='since' title='Stable since Rust version {0}'>{0}</div>", v)?
}
}
Ok(())
let ns_id = cx.derive_id(format!("{}.{}",
field.name.as_ref().unwrap(),
ItemType::StructField.name_space()));
- write!(w, "<span id=\"{id}\" class=\"{item_type} small-section-header\">
- <a href=\"#{id}\" class=\"anchor field\"></a>
- <span id=\"{ns_id}\" class='invisible'>
- <code>{name}: {ty}</code>
- </span></span>",
+ write!(w, "<span id=\"{id}\" class=\"{item_type} small-section-header\">\
+ <a href=\"#{id}\" class=\"anchor field\"></a>\
+ <code id=\"{ns_id}\">{name}: {ty}</code>\
+ </span>",
item_type = ItemType::StructField,
id = id,
ns_id = ns_id,
ItemType::Variant.name_space()));
write!(w, "<span id=\"{id}\" class=\"variant small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\"></a>\
- <span id='{ns_id}' class='invisible'><code>{name}",
+ <code id='{ns_id}'>{name}",
id = id,
ns_id = ns_id,
name = variant.name.as_ref().unwrap())?;
write!(w, ")")?;
}
}
- write!(w, "</code></span></span>")?;
+ write!(w, "</code></span>")?;
document(w, cx, variant)?;
use clean::{Variant, VariantKind};
ItemType::StructField.name_space()));
write!(w, "<span id=\"{id}\" class=\"variant small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\"></a>\
- <span id='{ns_id}' class='invisible'><code>{f}: {t}\
- </code></span></span>",
+ <code id='{ns_id}'>{f}: {t}\
+ </code></span>",
id = id,
ns_id = ns_id,
f = field.name.as_ref().unwrap(),
id, i.inner_impl())?;
}
write!(w, "<a href='#{}' class='anchor'></a>", id)?;
- write!(w, "</span></td><td><span class='out-of-band'>")?;
+ write!(w, "</td><td><span class='out-of-band'>")?;
let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]);
if let Some(l) = (Item { item: &i.impl_item, cx: cx }).src_href() {
write!(w, "<div class='ghost'></div>")?;
} else {
render_stability_since_raw(w, since, outer_version)?;
}
- write!(w, "</span></td></tr></tbody></table></h3>")?;
+ write!(w, "</span></td></tr></tbody></table></span></h3>")?;
if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
let mut ids = cx.id_map.borrow_mut();
write!(w, "<div class='docblock'>{}</div>",
let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class)?;
write!(w, "{}", spotlight_decl(decl)?)?;
- write!(w, "<span id='{}' class='invisible'>", ns_id)?;
- write!(w, "<table class='table-display'><tbody><tr><td><code>")?;
+ write!(w, "<table id='{}' class='table-display'><tbody><tr><td><code>", ns_id)?;
render_assoc_item(w, item, link.anchor(&id), ItemType::Impl)?;
write!(w, "</code>")?;
if let Some(l) = (Item { cx, item }).src_href() {
- write!(w, "</span></td><td><span class='out-of-band'>")?;
+ write!(w, "</td><td><span class='out-of-band'>")?;
write!(w, "<div class='ghost'></div>")?;
render_stability_since_raw(w, item.stable_since(), outer_version)?;
- write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>",
+ write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a></span>",
l, "goto source code")?;
} else {
write!(w, "</td><td>")?;
render_stability_since_raw(w, item.stable_since(), outer_version)?;
}
- write!(w, "</td></tr></tbody></table></span></h4>")?;
+ write!(w, "</td></tr></tbody></table></h4>")?;
}
}
clean::TypedefItem(ref tydef, _) => {
let id = cx.derive_id(format!("{}.{}", ItemType::AssociatedType, name));
let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class)?;
- write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
+ write!(w, "<code id='{}'>", ns_id)?;
assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id))?;
- write!(w, "</code></span></h4>\n")?;
+ write!(w, "</code></h4>")?;
}
clean::AssociatedConstItem(ref ty, ref default) => {
+ let mut version = String::new();
+
+ render_stability_since_raw(&mut version, item.stable_since(), outer_version)?;
+
let id = cx.derive_id(format!("{}.{}", item_type, name));
let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class)?;
- write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
+ if !version.is_empty() {
+ write!(w, "<table id='{}' class='table-display'><tbody><tr><td><code>", ns_id)?;
+ } else {
+ write!(w, "<code id='{}'>", ns_id)?;
+ }
assoc_const(w, item, ty, default.as_ref(), link.anchor(&id))?;
+ if !version.is_empty() {
+ write!(w, "</code>")?;
+ }
let src = if let Some(l) = (Item { cx, item }).src_href() {
+ if !version.is_empty() {
+ write!(w, "</td><td><span class='out-of-band'>")?;
+ write!(w, "<div class='ghost'></div>{}", version)?;
+ }
format!("<a class='srclink' href='{}' title='{}'>[src]</a>",
l, "goto source code")
} else {
+ if !version.is_empty() {
+ write!(w, "</td><td>{}", version)?;
+ }
String::new()
};
- write!(w, "</code>{}</span></h4>\n", src)?;
+ if version.is_empty() {
+ write!(w, "</code>{}</h4>", src)?;
+ } else {
+ write!(w, "{}</span></td></tr></tbody></table></h4>", src)?;
+ }
}
clean::AssociatedTypeItem(ref bounds, ref default) => {
let id = cx.derive_id(format!("{}.{}", item_type, name));
let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class)?;
- write!(w, "<span id='{}' class='invisible'><code>", ns_id)?;
+ write!(w, "<code id='{}'>", ns_id)?;
assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id))?;
- write!(w, "</code></span></h4>\n")?;
+ write!(w, "</code></h4>")?;
}
clean::StrippedItem(..) => return Ok(()),
_ => panic!("can't make docs for trait item with name {:?}", item.name)
text-decoration: underline;
}
-.invisible > .srclink {
+.invisible > .srclink, h4 > code + .srclink {
position: absolute;
top: 0;
right: 0;
display: list-item;
}
-.stab .microscope {
+.stab .emoji {
font-size: 1.5em;
}
color: #D2991D;
}
+.stab.internal a {
+ color: #304FFE;
+}
+
a.test-arrow {
color: #dedede;
}
}
.stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #404040; }
+.stab.internal { background: #FFB9B3; border-color: #B71C1C; color: #404040; }
.stab.deprecated { background: #F3DFFF; border-color: #7F0087; color: #404040; }
.stab.portability { background: #C4ECFF; border-color: #7BA5DB; color: #404040; }
color: #3873AD;
}
+.stab.internal a {
+ color: #304FFE;
+}
+
a.test-arrow {
color: #f5f5f5;
}
}
.stab.unstable { background: #FFF5D6; border-color: #FFC600; }
+.stab.internal { background: #FFB9B3; border-color: #B71C1C; }
.stab.deprecated { background: #F3DFFF; border-color: #7F0087; }
.stab.portability { background: #C4ECFF; border-color: #7BA5DB; }
#![feature(cfg_target_vendor)]
#![feature(char_error_internals)]
#![feature(compiler_builtins_lib)]
+#![feature(concat_idents)]
#![feature(const_int_ops)]
#![feature(const_ip)]
#![feature(const_raw_ptr_deref)]
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ // On WASM, `TcpStream` is uninhabited (as it's unsupported) and so
+ // the `a` variable here is technically unused.
+ #[cfg_attr(target_arch = "wasm32", allow(unused_variables))]
self.0.accept().map(|(a, b)| (TcpStream(a), b))
}
{
lang_start_internal(&move || main().report(), argc, argv)
}
-
-/// Function used for reverting changes to the main stack before setrlimit().
-/// This is POSIX (non-Linux) specific and unlikely to be directly stabilized.
-#[unstable(feature = "rustc_stack_internals", issue = "0")]
-pub unsafe fn deinit_stack_guard() {
- ::sys::thread::guard::deinit();
-}
-
-/// Function used for resetting the main stack guard address after setrlimit().
-/// This is POSIX specific and unlikely to be directly stabilized.
-#[unstable(feature = "rustc_stack_internals", issue = "0")]
-pub unsafe fn update_stack_guard() {
- let main_guard = ::sys::thread::guard::init();
- ::sys_common::thread_info::reset_guard(main_guard);
-}
pub unsafe fn init() -> Option<Guard> {
None
}
- pub unsafe fn deinit() {}
}
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
- pub unsafe fn deinit() {}
}
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
- pub unsafe fn deinit() {}
}
// Linux. This was added in 2.6.28, however, and because we support
// 2.6.18 we must detect this support dynamically.
if cfg!(target_os = "linux") {
- weak! {
- fn accept4(c_int, *mut sockaddr, *mut socklen_t, c_int) -> c_int
+ syscall! {
+ fn accept4(
+ fd: c_int,
+ addr: *mut sockaddr,
+ addr_len: *mut socklen_t,
+ flags: c_int
+ ) -> c_int
}
- if let Some(accept) = accept4.get() {
- let res = cvt_r(|| unsafe {
- accept(self.0.raw(), storage, len, SOCK_CLOEXEC)
- });
- match res {
- Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
- Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
- Err(e) => return Err(e),
- }
+ let res = cvt_r(|| unsafe {
+ accept4(self.0.raw(), storage, len, SOCK_CLOEXEC)
+ });
+ match res {
+ Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
+ Err(e) => return Err(e),
}
}
}
/// Sets the platform-specific value of errno
-#[cfg(any(target_os = "solaris", target_os = "fuchsia"))] // only needed for readdir so far
+#[cfg(all(not(target_os = "linux"),
+ not(target_os = "dragonfly")))] // needed for readdir and syscall!
pub fn set_errno(e: i32) {
unsafe {
*errno_location() = e as c_int
unsafe { errno as i32 }
}
+#[cfg(target_os = "dragonfly")]
+pub fn set_errno(e: i32) {
+ extern {
+ #[thread_local]
+ static mut errno: c_int;
+ }
+
+ unsafe {
+ errno = e;
+ }
+}
+
/// Gets a detailed string description for the given error number.
pub fn error_string(errno: i32) -> String {
extern {
pub struct AnonPipe(FileDesc);
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
- weak! { fn pipe2(*mut c_int, c_int) -> c_int }
+ syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int }
static INVALID: AtomicBool = ATOMIC_BOOL_INIT;
let mut fds = [0; 2];
!INVALID.load(Ordering::SeqCst)
{
- if let Some(pipe) = pipe2.get() {
- // Note that despite calling a glibc function here we may still
- // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
- // emulate on older kernels, so if you happen to be running on
- // an older kernel you may see `pipe2` as a symbol but still not
- // see the syscall.
- match cvt(unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
- Ok(_) => {
- return Ok((AnonPipe(FileDesc::new(fds[0])),
- AnonPipe(FileDesc::new(fds[1]))));
- }
- Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
- INVALID.store(true, Ordering::SeqCst);
- }
- Err(e) => return Err(e),
+ // Note that despite calling a glibc function here we may still
+ // get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
+ // emulate on older kernels, so if you happen to be running on
+ // an older kernel you may see `pipe2` as a symbol but still not
+ // see the syscall.
+ match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
+ Ok(_) => {
+ return Ok((AnonPipe(FileDesc::new(fds[0])),
+ AnonPipe(FileDesc::new(fds[1]))));
+ }
+ Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
+ INVALID.store(true, Ordering::SeqCst);
}
+ Err(e) => return Err(e),
}
}
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
pub type Guard = Range<usize>;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
- pub unsafe fn deinit() {}
}
}
}
- pub unsafe fn deinit() {
- if !cfg!(target_os = "linux") {
- if let Some(stackaddr) = get_stack_start_aligned() {
- // Remove the protection on the guard page.
- // FIXME: we cannot unmap the page, because when we mmap()
- // above it may be already mapped by the OS, which we can't
- // detect from mmap()'s return value. If we unmap this page,
- // it will lead to failure growing stack size on platforms like
- // macOS. Instead, just restore the page to a writable state.
- // This ain't Linux, so we probably don't need to care about
- // execstack.
- let result = mprotect(stackaddr, PAGE_SIZE, PROT_READ | PROT_WRITE);
-
- if result != 0 {
- panic!("unable to reset the guard page");
- }
- }
- }
- }
-
#[cfg(any(target_os = "macos",
target_os = "bitrig",
target_os = "openbsd",
};
libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) as usize
}
+
+#[cfg(not(target_os = "linux"))]
+macro_rules! syscall {
+ (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+ unsafe fn $name($($arg_name: $t),*) -> $ret {
+ use libc;
+ use super::os;
+
+ weak! { fn $name($($t),*) -> $ret }
+
+ if let Some(fun) = $name.get() {
+ fun($($arg_name),*)
+ } else {
+ os::set_errno(libc::ENOSYS);
+ -1
+ }
+ }
+ )
+}
+
+#[cfg(target_os = "linux")]
+macro_rules! syscall {
+ (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+ unsafe fn $name($($arg_name:$t),*) -> $ret {
+ // This looks like a hack, but concat_idents only accepts idents
+ // (not paths).
+ use libc::*;
+
+ syscall(
+ concat_idents!(SYS_, $name),
+ $($arg_name as c_long),*
+ ) as $ret
+ }
+ )
+}
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
- pub unsafe fn deinit() {}
}
cfg_if! {
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
- pub unsafe fn deinit() {}
}
pub trait Resolver {
fn next_node_id(&mut self) -> ast::NodeId;
fn get_module_scope(&mut self, id: ast::NodeId) -> Mark;
- fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item>;
fn visit_ast_fragment_with_placeholders(&mut self, mark: Mark, fragment: &AstFragment,
derives: &[Mark]);
impl Resolver for DummyResolver {
fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID }
fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() }
- fn eliminate_crate_var(&mut self, item: P<ast::Item>) -> P<ast::Item> { item }
fn visit_ast_fragment_with_placeholders(&mut self, _invoc: Mark, _fragment: &AstFragment,
_derives: &[Mark]) {}
if i != 0 {
path_str.push_str("::");
}
-
- if segment.ident.name != keywords::PathRoot.name() &&
- segment.ident.name != keywords::DollarCrate.name()
- {
+ if segment.ident.name != keywords::PathRoot.name() {
path_str.push_str(&segment.ident.as_str())
}
}
}
}
+ #[inline]
pub fn source_map(&self) -> &SourceMap {
&self.source_map
}
(&Shebang(a), &Shebang(b)) => a == b,
(&Lifetime(a), &Lifetime(b)) => a.name == b.name,
- (&Ident(a, b), &Ident(c, d)) => a.name == c.name && b == d,
+ (&Ident(a, b), &Ident(c, d)) => b == d && (a.name == c.name ||
+ a.name == keywords::DollarCrate.name() ||
+ c.name == keywords::DollarCrate.name()),
(&Literal(ref a, b), &Literal(ref c, d)) => {
b == d && a.probably_equal_for_proc_macro(c)
use attr;
use source_map::{self, SourceMap, Spanned};
use syntax_pos::{self, BytePos};
-use syntax_pos::hygiene::{Mark, SyntaxContext};
use parse::token::{self, BinOpToken, Token};
use parse::lexer::comments;
use parse::{self, ParseSess};
if i > 0 {
self.writer().word("::")?
}
- if segment.ident.name != keywords::PathRoot.name() &&
- segment.ident.name != keywords::DollarCrate.name()
- {
- self.writer().word(segment.ident.as_str().get())?;
- } else if segment.ident.name == keywords::DollarCrate.name() {
- self.print_dollar_crate(segment.ident.span.ctxt())?;
+ if segment.ident.name != keywords::PathRoot.name() {
+ if segment.ident.name == keywords::DollarCrate.name() {
+ self.print_dollar_crate(segment.ident)?;
+ } else {
+ self.writer().word(segment.ident.as_str().get())?;
+ }
}
}
Ok(())
fn nbsp(&mut self) -> io::Result<()> { self.writer().word(" ") }
- fn print_dollar_crate(&mut self, mut ctxt: SyntaxContext) -> io::Result<()> {
- if let Some(mark) = ctxt.adjust(Mark::root()) {
- // Make a best effort to print something that complies
- if mark.is_builtin() {
- if let Some(name) = std_inject::injected_crate_name() {
- self.writer().word("::")?;
- self.writer().word(name)?;
- }
- }
+ // AST pretty-printer is used as a fallback for turning AST structures into token streams for
+ // proc macros. Additionally, proc macros may stringify their input and expect it survive the
+ // stringification (especially true for proc macro derives written between Rust 1.15 and 1.30).
+ // So we need to somehow pretty-print `$crate` in paths in a way preserving at least some of
+ // its hygiene data, most importantly name of the crate it refers to.
+ // As a result we print `$crate` as `crate` if it refers to the local crate
+ // and as `::other_crate_name` if it refers to some other crate.
+ fn print_dollar_crate(&mut self, ident: ast::Ident) -> io::Result<()> {
+ let name = ident.span.ctxt().dollar_crate_name();
+ if !ast::Ident::with_empty_ctxt(name).is_path_segment_keyword() {
+ self.writer().word("::")?;
}
- Ok(())
+ self.writer().word(name.as_str().get())
}
}
colons_before_params: bool)
-> io::Result<()>
{
- if segment.ident.name != keywords::PathRoot.name() &&
- segment.ident.name != keywords::DollarCrate.name() {
- self.print_ident(segment.ident)?;
+ if segment.ident.name != keywords::PathRoot.name() {
+ if segment.ident.name == keywords::DollarCrate.name() {
+ self.print_dollar_crate(segment.ident)?;
+ } else {
+ self.print_ident(segment.ident)?;
+ }
if let Some(ref args) = segment.args {
self.print_generic_args(args, colons_before_params)?;
}
- } else if segment.ident.name == keywords::DollarCrate.name() {
- self.print_dollar_crate(segment.ident.span.ctxt())?;
}
Ok(())
}
| TokenTree::Token(_, Token::Semi)
// The pretty printer collapses whitespace arbitrarily and can
// introduce whitespace from `NoDelim`.
- | TokenTree::Token(_, Token::Whitespace) => false,
+ | TokenTree::Token(_, Token::Whitespace)
+ // The pretty printer can turn `$crate` into `::crate_name`
+ | TokenTree::Token(_, Token::ModSep) => false,
_ => true
}
}
// Mark attributes as known, and used.
MarkAttrs(&self.attrs).visit_item(&item);
- let item = ecx.resolver.eliminate_crate_var(item);
let token = Token::interpolated(token::NtItem(item));
let input = tokenstream::TokenTree::Token(DUMMY_SP, token).into();
$($field $(: $value)*,)*
span,
})
- )
+ );
+ ($ty:ident::$method:ident($($value:expr),*)) => (
+ TokenTree::$ty(self::$ty::$method($($value,)* span))
+ );
}
macro_rules! op {
($a:expr) => {
- tt!(Punct { ch: $a, joint })
+ tt!(Punct::new($a, joint))
};
($a:expr, $b:expr) => {{
- stack.push(tt!(Punct { ch: $b, joint }));
- tt!(Punct {
- ch: $a,
- joint: true
- })
+ stack.push(tt!(Punct::new($b, joint)));
+ tt!(Punct::new($a, true))
}};
($a:expr, $b:expr, $c:expr) => {{
- stack.push(tt!(Punct { ch: $c, joint }));
- stack.push(tt!(Punct {
- ch: $b,
- joint: true
- }));
- tt!(Punct {
- ch: $a,
- joint: true
- })
+ stack.push(tt!(Punct::new($c, joint)));
+ stack.push(tt!(Punct::new($b, true)));
+ tt!(Punct::new($a, true))
}};
}
Question => op!('?'),
SingleQuote => op!('\''),
- Ident(ident, is_raw) => tt!(Ident {
- sym: ident.name,
- is_raw
- }),
+ Ident(ident, false) if ident.name == keywords::DollarCrate.name() =>
+ tt!(Ident::dollar_crate()),
+ Ident(ident, is_raw) => tt!(Ident::new(ident.name, is_raw)),
Lifetime(ident) => {
let ident = ident.without_first_quote();
- stack.push(tt!(Ident {
- sym: ident.name,
- is_raw: false
- }));
- tt!(Punct {
- ch: '\'',
- joint: true
- })
+ stack.push(tt!(Ident::new(ident.name, false)));
+ tt!(Punct::new('\'', true))
}
Literal(lit, suffix) => tt!(Literal { lit, suffix }),
DocComment(c) => {
span: DelimSpan::from_single(span),
}));
if style == ast::AttrStyle::Inner {
- stack.push(tt!(Punct {
- ch: '!',
- joint: false
- }));
+ stack.push(tt!(Punct::new('!', false)));
}
- tt!(Punct {
- ch: '#',
- joint: false
- })
+ tt!(Punct::new('#', false))
}
Interpolated(_) => {
)
.into();
}
- TokenTree::Ident(self::Ident { sym, span, is_raw }) => {
+ TokenTree::Ident(self::Ident { sym, is_raw, span }) => {
let token = Ident(ast::Ident::new(sym, span), is_raw);
return tokenstream::TokenTree::Token(span, token).into();
}
span: Span,
}
+impl Punct {
+ fn new(ch: char, joint: bool, span: Span) -> Punct {
+ const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^',
+ '&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\''];
+ if !LEGAL_CHARS.contains(&ch) {
+ panic!("unsupported character `{:?}`", ch)
+ }
+ Punct { ch, joint, span }
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Ident {
sym: Symbol,
- span: Span,
is_raw: bool,
+ span: Span,
+}
+
+impl Ident {
+ fn is_valid(string: &str) -> bool {
+ let mut chars = string.chars();
+ if let Some(start) = chars.next() {
+ (start == '_' || start.is_xid_start())
+ && chars.all(|cont| cont == '_' || cont.is_xid_continue())
+ } else {
+ false
+ }
+ }
+ fn new(sym: Symbol, is_raw: bool, span: Span) -> Ident {
+ let string = sym.as_str().get();
+ if !Self::is_valid(string) {
+ panic!("`{:?}` is not a valid identifier", string)
+ }
+ if is_raw {
+ let normalized_sym = Symbol::intern(string);
+ if normalized_sym == keywords::Underscore.name() ||
+ ast::Ident::with_empty_ctxt(normalized_sym).is_path_segment_keyword() {
+ panic!("`{:?}` is not a valid raw identifier", string)
+ }
+ }
+ Ident { sym, is_raw, span }
+ }
+ fn dollar_crate(span: Span) -> Ident {
+ // `$crate` is accepted as an ident only if it comes from the compiler.
+ Ident { sym: keywords::DollarCrate.name(), is_raw: false, span }
+ }
}
// FIXME(eddyb) `Literal` should not expose internal `Debug` impls.
impl server::Punct for Rustc<'_> {
fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct {
- Punct {
- ch,
- joint: spacing == Spacing::Joint,
- span: server::Span::call_site(self),
- }
+ Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self))
}
fn as_char(&mut self, punct: Self::Punct) -> char {
punct.ch
impl server::Ident for Rustc<'_> {
fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
- let sym = Symbol::intern(string);
- if is_raw
- && (sym == keywords::Underscore.name()
- || ast::Ident::with_empty_ctxt(sym).is_path_segment_keyword())
- {
- panic!("`{:?}` is not a valid raw identifier", string)
- }
- Ident { sym, span, is_raw }
+ Ident::new(Symbol::intern(string), is_raw, span)
}
fn span(&mut self, ident: Self::Ident) -> Self::Span {
ident.span
use GLOBALS;
use Span;
use edition::{Edition, DEFAULT_EDITION};
-use symbol::Symbol;
+use symbol::{keywords, Symbol};
use serialize::{Encodable, Decodable, Encoder, Decoder};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use std::fmt;
+use std::{fmt, mem};
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
opaque: SyntaxContext,
// This context, but with all transparent marks filtered away.
opaque_and_semitransparent: SyntaxContext,
+ // Name of the crate to which `$crate` with this context would resolve.
+ dollar_crate_name: Symbol,
}
/// A mark is a unique id associated with a macro expansion.
struct MarkData {
parent: Mark,
default_transparency: Transparency,
- is_builtin: bool,
expn_info: Option<ExpnInfo>,
}
parent,
// By default expansions behave like `macro_rules`.
default_transparency: Transparency::SemiTransparent,
- is_builtin: false,
expn_info: None,
});
Mark(data.marks.len() as u32 - 1)
HygieneData::with(|data| data.marks[self.0 as usize].default_transparency = transparency)
}
- #[inline]
- pub fn is_builtin(self) -> bool {
- assert_ne!(self, Mark::root());
- HygieneData::with(|data| data.marks[self.0 as usize].is_builtin)
- }
-
- #[inline]
- pub fn set_is_builtin(self, is_builtin: bool) {
- assert_ne!(self, Mark::root());
- HygieneData::with(|data| data.marks[self.0 as usize].is_builtin = is_builtin)
- }
-
pub fn is_descendant_of(mut self, ancestor: Mark) -> bool {
HygieneData::with(|data| {
while self != ancestor {
// If the root is opaque, then loops searching for an opaque mark
// will automatically stop after reaching it.
default_transparency: Transparency::Opaque,
- is_builtin: true,
expn_info: None,
}],
syntax_contexts: vec![SyntaxContextData {
prev_ctxt: SyntaxContext(0),
opaque: SyntaxContext(0),
opaque_and_semitransparent: SyntaxContext(0),
+ dollar_crate_name: keywords::DollarCrate.name(),
}],
markings: FxHashMap::default(),
default_edition: DEFAULT_EDITION,
data.marks.push(MarkData {
parent: Mark::root(),
default_transparency: Transparency::SemiTransparent,
- is_builtin: false,
expn_info: Some(expansion_info),
});
prev_ctxt: SyntaxContext::empty(),
opaque: SyntaxContext::empty(),
opaque_and_semitransparent: SyntaxContext::empty(),
+ dollar_crate_name: keywords::DollarCrate.name(),
});
SyntaxContext(data.syntax_contexts.len() as u32 - 1)
})
prev_ctxt,
opaque: new_opaque,
opaque_and_semitransparent: new_opaque,
+ dollar_crate_name: keywords::DollarCrate.name(),
});
new_opaque
});
prev_ctxt,
opaque,
opaque_and_semitransparent: new_opaque_and_semitransparent,
+ dollar_crate_name: keywords::DollarCrate.name(),
});
new_opaque_and_semitransparent
});
prev_ctxt,
opaque,
opaque_and_semitransparent,
+ dollar_crate_name: keywords::DollarCrate.name(),
});
new_opaque_and_semitransparent_and_transparent
})
pub fn outer(self) -> Mark {
HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark)
}
+
+ pub fn dollar_crate_name(self) -> Symbol {
+ HygieneData::with(|data| data.syntax_contexts[self.0 as usize].dollar_crate_name)
+ }
+
+ pub fn set_dollar_crate_name(self, dollar_crate_name: Symbol) {
+ HygieneData::with(|data| {
+ let prev_dollar_crate_name = mem::replace(
+ &mut data.syntax_contexts[self.0 as usize].dollar_crate_name, dollar_crate_name
+ );
+ assert!(dollar_crate_name == prev_dollar_crate_name ||
+ prev_dollar_crate_name == keywords::DollarCrate.name(),
+ "$crate name is reset for a syntax context");
+ })
+ }
}
impl fmt::Debug for SyntaxContext {
+++ /dev/null
-// 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 <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.
-
-// NOTE Instantiating an empty enum is UB. This test may break in the future.
-
-// LLDB can't handle zero-sized values
-// ignore-lldb
-
-
-// Require LLVM with DW_TAG_variant_part and a gdb that can read it.
-// gdb 8.2.0 crashes on this test case, see
-// https://sourceware.org/bugzilla/show_bug.cgi?id=23626
-// This will be fixed in the next release, which will be >= 8.2.1.
-// min-system-llvm-version: 7.0
-// min-gdb-version: 8.2.1
-
-// compile-flags:-g
-// gdb-command:run
-
-// gdb-command:print first
-// gdbr-check:$1 = nil_enum::ANilEnum {<No data fields>}
-
-// gdb-command:print second
-// gdbr-check:$2 = nil_enum::AnotherNilEnum {<No data fields>}
-
-#![allow(unused_variables)]
-#![feature(omit_gdb_pretty_printer_section)]
-#![feature(maybe_uninit)]
-#![omit_gdb_pretty_printer_section]
-
-use std::mem::MaybeUninit;
-
-enum ANilEnum {}
-enum AnotherNilEnum {}
-
-// This test relies on gdbg printing the string "{<No data fields>}" for empty
-// structs (which may change some time)
-// The error from gdbr is expected since nil enums are not supposed to exist.
-fn main() {
- unsafe {
- let first: ANilEnum = MaybeUninit::uninitialized().into_inner();
- let second: AnotherNilEnum = MaybeUninit::uninitialized().into_inner();
-
- zzz(); // #break
- }
-}
-
-fn zzz() {()}
// ...
// _14 = &mut (*_10);
// Retag(_14);
-// EscapeToRaw(move _14);
// _13 = move _14 as *mut i32 (Misc);
+// Retag([raw] _13);
// ...
// _17 = move _18(move _19) -> bb2;
// }
--- /dev/null
+#![feature(never_type)]
+
+pub enum Void {}
+
+#[no_mangle]
+pub fn process_never(input: *const !) {
+ let _input = unsafe { &*input };
+}
+
+#[no_mangle]
+pub fn process_void(input: *const Void) {
+ let _input = unsafe { &*input };
+ // In the future, this should end with `unreachable`, but we currently only do
+ // unreachability analysis for `!`.
+}
+
+fn main() {}
+
+// END RUST SOURCE
+//
+// START rustc.process_never.SimplifyLocals.after.mir
+// bb0: {
+// StorageLive(_2);
+// _2 = &(*_1);
+// StorageDead(_2);
+// unreachable;
+// }
+// END rustc.process_never.SimplifyLocals.after.mir
+//
+// START rustc.process_void.SimplifyLocals.after.mir
+// bb0: {
+// StorageLive(_2);
+// _2 = &(*_1);
+// StorageDead(_2);
+// return;
+// }
+// END rustc.process_void.SimplifyLocals.after.mir
- ((::fmt::format as
- for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<::fmt::Arguments>::new_v1
+ (($crate::fmt::format as
+ for<'r> fn(std::fmt::Arguments<'r>) -> std::string::String {std::fmt::format})(((<$crate::fmt::Arguments>::new_v1
as
fn(&[&str], &[std::fmt::ArgumentV1<'_>]) -> std::fmt::Arguments<'_> {std::fmt::Arguments<'_>::new_v1})((&([("test"
as
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-#![feature(arbitrary_self_types)]
#![feature(pin)]
#![feature(rustc_attrs)]
fn by_arc(self: Arc<Self>) -> i64;
fn by_pin_mut(self: Pin<&mut Self>) -> i64;
fn by_pin_box(self: Pin<Box<Self>>) -> i64;
+ fn by_pin_pin_pin_ref(self: Pin<Pin<Pin<&Self>>>) -> i64;
}
impl Trait for i64 {
fn by_pin_box(self: Pin<Box<Self>>) -> i64 {
*self
}
+ fn by_pin_pin_pin_ref(self: Pin<Pin<Pin<&Self>>>) -> i64 {
+ *self
+ }
}
fn main() {
let pin_box = Into::<Pin<Box<i64>>>::into(Box::new(4i64)) as Pin<Box<dyn Trait>>;
assert_eq!(4, pin_box.by_pin_box());
+
+ let value = 5i64;
+ let pin_pin_pin_ref = Pin::new(Pin::new(Pin::new(&value))) as Pin<Pin<Pin<&dyn Trait>>>;
+ assert_eq!(5, pin_pin_pin_ref.by_pin_pin_pin_ref());
}
#![feature(slice_patterns)]
#![allow(unreachable_patterns)]
#![allow(unreachable_code)]
+#![allow(unused_variables)]
#[allow(dead_code)]
fn foo(z: !) {
--- /dev/null
+// 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 <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.
+
+use std::ops::Deref;
+
+pub struct Pin<P>(P);
+
+impl<P, T> Deref for Pin<P>
+where
+ P: Deref<Target=T>,
+{
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &*self.0
+ }
+}
+
+impl<P> Pin<P> {
+ fn poll(self) {}
+}
+
+fn main() {
+ let mut unit = ();
+ let pin = Pin(&mut unit);
+ pin.poll();
+}
// run-pass
#![allow(dead_code)]
#![allow(unused_variables)]
+#![recursion_limit = "128"]
// this used to cause exponential code-size blowup during LLVM passes.
#![feature(test)]
--- /dev/null
+// 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 <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.
+
+// ignore-tidy-linelength
+
+#![crate_name = "foo"]
+
+#![feature(staged_api)]
+
+#![stable(since="1.1.1", feature="rust1")]
+
+#[stable(since="1.1.1", feature="rust1")]
+pub struct SomeStruct;
+
+impl SomeStruct {
+ // @has 'foo/struct.SomeStruct.html' '//*[@id="SOME_CONST.v"]//div[@class="since"]' '1.1.2'
+ #[stable(since="1.1.2", feature="rust2")]
+ pub const SOME_CONST: usize = 0;
+}
// @has assoc_types/trait.Index.html
pub trait Index<I: ?Sized> {
// @has - '//*[@id="associatedtype.Output"]//code' 'type Output: ?Sized'
- // @has - '//*[@id="Output.t"]//code' 'type Output: ?Sized'
+ // @has - '//code[@id="Output.t"]' 'type Output: ?Sized'
type Output: ?Sized;
- // @has - '//*[@id="index.v"]//code' 'fn index'
+ // @has - '//code[@id="index.v"]' 'fn index'
// @has - '//*[@id="tymethod.index"]//code' \
// "fn index<'a>(&'a self, index: I) -> &'a Self::Output"
// @has - '//*[@id="tymethod.index"]//code//a[@href="../assoc_types/trait.Index.html#associatedtype.Output"]' \
--- /dev/null
+// 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 <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.
+
+#![crate_name = "foo"]
+
+#![unstable(feature = "humans",
+ reason = "who ever let humans program computers, we're apparently really bad at it",
+ issue = "0")]
+
+#![feature(rustc_const_unstable, const_fn, foo, foo2)]
+#![feature(min_const_unsafe_fn)]
+#![feature(staged_api)]
+
+// @has 'foo/fn.foo.html' '//pre' 'pub unsafe fn foo() -> u32'
+#[stable(feature = "rust1", since = "1.0.0")]
+#[rustc_const_unstable(feature="foo")]
+pub const unsafe fn foo() -> u32 { 42 }
+
+// @has 'foo/fn.foo2.html' '//pre' 'pub fn foo2() -> u32'
+#[unstable(feature = "humans", issue="0")]
+pub const fn foo2() -> u32 { 42 }
+
+// @has 'foo/fn.bar2.html' '//pre' 'pub const fn bar2() -> u32'
+#[stable(feature = "rust1", since = "1.0.0")]
+pub const fn bar2() -> u32 { 42 }
+
+// @has 'foo/fn.foo2_gated.html' '//pre' 'pub unsafe fn foo2_gated() -> u32'
+#[unstable(feature = "foo2", issue="0")]
+pub const unsafe fn foo2_gated() -> u32 { 42 }
+
+// @has 'foo/fn.bar2_gated.html' '//pre' 'pub const unsafe fn bar2_gated() -> u32'
+#[stable(feature = "rust1", since = "1.0.0")]
+pub const unsafe fn bar2_gated() -> u32 { 42 }
+
+// @has 'foo/fn.bar_not_gated.html' '//pre' 'pub unsafe fn bar_not_gated() -> u32'
+pub const unsafe fn bar_not_gated() -> u32 { 42 }
#![feature(deprecated)]
+// @matches deprecated/index.html '//*[@class="docblock-short"]' \
+// '^\[Deprecated\] Deprecated docs'
// @has deprecated/struct.S.html '//*[@class="stab deprecated"]' \
// 'Deprecated since 1.0.0: text'
+/// Deprecated docs
#[deprecated(since = "1.0.0", note = "text")]
pub struct S;
+
+// @matches deprecated/index.html '//*[@class="docblock-short"]' '^Docs'
+/// Docs
+pub struct T;
+
+// @matches deprecated/struct.U.html '//*[@class="stab deprecated"]' \
+// 'Deprecated since 1.0.0$'
+#[deprecated(since = "1.0.0")]
+pub struct U;
+
+// @matches deprecated/struct.V.html '//*[@class="stab deprecated"]' \
+// 'Deprecated: text$'
+#[deprecated(note = "text")]
+pub struct V;
+
+// @matches deprecated/struct.W.html '//*[@class="stab deprecated"]' \
+// 'Deprecated$'
+#[deprecated]
+pub struct W;
--- /dev/null
+// compile-flags: -Z force-unstable-if-unmarked
+
+// @matches internal/index.html '//*[@class="docblock-short"]' \
+// '^\[Internal\] Docs'
+// @has internal/struct.S.html '//*[@class="stab internal"]' \
+// 'This is an internal compiler API. (rustc_private)'
+/// Docs
+pub struct S;
+
+fn main() {}
#![unstable(feature="test", issue="27759")]
// @has issue_27759/unstable/index.html
-// @has - '<code>test </code>'
-// @has - '<a href="http://issue_url/27759">#27759</a>'
+// @has - '<code>test</code> <a href="http://issue_url/27759">#27759</a>'
#[unstable(feature="test", issue="27759")]
pub mod unstable {
// @has issue_27759/unstable/fn.issue.html
- // @has - '<code>test_function </code>'
- // @has - '<a href="http://issue_url/1234567890">#1234567890</a>'
- #[unstable(feature="test_function", issue="1234567890")]
+ // @has - '<code>test_function</code> <a href="http://issue_url/12345">#12345</a>'
+ #[unstable(feature="test_function", issue="12345")]
pub fn issue() {}
}
#![unstable(feature="test", issue = "32374")]
-// @has issue_32374/index.html '//*[@class="docblock-short"]' \
-// '[Deprecated] [Experimental]'
+// @matches issue_32374/index.html '//*[@class="docblock-short"]' \
+// '^\[Deprecated\] \[Experimental\] Docs'
// @has issue_32374/struct.T.html '//*[@class="stab deprecated"]' \
// 'Deprecated since 1.0.0: text'
-// @has - '<code>test </code>'
-// @has - '<a href="http://issue_url/32374">#32374</a>'
+// @has - '<code>test</code> <a href="http://issue_url/32374">#32374</a>'
// @matches issue_32374/struct.T.html '//*[@class="stab unstable"]' \
-// '🔬 This is a nightly-only experimental API. \(test #32374\)$'
+// '🔬 This is a nightly-only experimental API. \(test #32374\)$'
+/// Docs
#[rustc_deprecated(since = "1.0.0", reason = "text")]
#[unstable(feature = "test", issue = "32374")]
pub struct T;
--- /dev/null
+// 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 <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-pass
+
+#![feature(never_type)]
+#![allow(unreachable_code)]
+
+use std::error::Error;
+use std::mem;
+
+fn raw_ptr_box<T>(t: T) -> *mut T {
+ panic!()
+}
+
+fn foo(x: !) -> Box<dyn Error> {
+ /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
+}
+
+fn foo_raw_ptr(x: !) -> *mut dyn Error {
+ /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
+}
+
+fn no_coercion(d: *mut dyn Error) -> *mut dyn Error {
+ /* an unsize coercion won't compile here, and it is indeed not used
+ because there is nothing requiring the _ to be Sized */
+ d as *mut _
+}
+
+trait Xyz {}
+struct S;
+struct T;
+impl Xyz for S {}
+impl Xyz for T {}
+
+fn foo_no_never() {
+ let mut x /* : Option<S> */ = None;
+ let mut first_iter = false;
+ loop {
+ if !first_iter {
+ let y: Box<dyn Xyz>
+ = /* Box<$0> is coerced to Box<Xyz> here */ Box::new(x.unwrap());
+ }
+
+ x = Some(S);
+ first_iter = true;
+ }
+
+ let mut y : Option<S> = None;
+ // assert types are equal
+ mem::swap(&mut x, &mut y);
+}
+
+fn main() {
+}
--- /dev/null
+#![crate_type = "rlib"]
+#![feature(fundamental)]
+
+pub trait Misc {}
+
+#[fundamental]
+pub trait Fundamental<T> {}
--- /dev/null
+// Check that trait objects from #[fundamental] traits are not
+// treated as #[fundamental] types - the 2 meanings of #[fundamental]
+// are distinct.
+
+// aux-build:coherence_fundamental_trait_lib.rs
+
+extern crate coherence_fundamental_trait_lib;
+
+use coherence_fundamental_trait_lib::{Fundamental, Misc};
+
+pub struct Local;
+impl Misc for dyn Fundamental<Local> {}
+//~^ ERROR E0117
+
+fn main() {}
--- /dev/null
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+ --> $DIR/coherence-fundamental-trait-objects.rs:12:1
+ |
+LL | impl Misc for dyn Fundamental<Local> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl doesn't use types inside crate
+ |
+ = note: the impl does not reference any types defined in this crate
+ = note: define and implement a trait or new type instead
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0117`.
-// 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 <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.
-
#![feature(const_transmute)]
#![allow(const_err)] // make sure we cannot allow away the errors tested here
#[derive(Copy, Clone)]
enum Bar {}
-const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
+union TransmuteUnion<A: Clone + Copy, B: Clone + Copy> {
+ a: A,
+ b: B,
+}
+
+const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b };
//~^ ERROR it is undefined behavior to use this value
const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
//~^ ERROR it is undefined behavior to use this value
-const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) };
+const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b };
//~^ ERROR it is undefined behavior to use this value
-fn main() {
-}
+fn main() {}
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-uninhabit.rs:19:1
+ --> $DIR/ub-uninhabit.rs:14:1
|
-LL | const BAD_BAD_BAD: Bar = unsafe { mem::transmute(()) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
+LL | const BAD_BAD_BAD: Bar = unsafe { (TransmuteUnion::<(), Bar> { a: () }).b };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-uninhabit.rs:22:1
+ --> $DIR/ub-uninhabit.rs:17:1
|
LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
error[E0080]: it is undefined behavior to use this value
- --> $DIR/ub-uninhabit.rs:25:1
+ --> $DIR/ub-uninhabit.rs:20:1
|
-LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { mem::transmute(()) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at [0]
+LL | const BAD_BAD_ARRAY: [Bar; 1] = unsafe { (TransmuteUnion::<(), [Bar; 1]> { a: () }).b };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
--> $DIR/validate_never_arrays.rs:3:1
|
LL | const FOO: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; //~ ERROR undefined behavior
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>[0]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use std::rc::Rc;
+use std::{
+ ops::Deref,
+};
+
+struct Ptr<T: ?Sized>(Box<T>);
+
+impl<T: ?Sized> Deref for Ptr<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &*self.0
+ }
+}
trait Foo {
- fn foo(self: Rc<Box<Self>>); //~ ERROR arbitrary `self` types are unstable
+ fn foo(self: Ptr<Self>); //~ ERROR `Ptr<Self>` cannot be used as the type of `self` without
}
struct Bar;
impl Foo for Bar {
- fn foo(self: Rc<Box<Self>>) {} //~ ERROR arbitrary `self` types are unstable
+ fn foo(self: Ptr<Self>) {} //~ ERROR `Ptr<Bar>` cannot be used as the type of `self` without
}
impl Bar {
- fn bar(self: Box<Rc<Self>>) {} //~ ERROR arbitrary `self` types are unstable
+ fn bar(self: Box<Ptr<Self>>) {} //~ ERROR `std::boxed::Box<Ptr<Bar>>` cannot be used as the
}
fn main() {}
-error[E0658]: arbitrary `self` types are unstable (see issue #44874)
- --> $DIR/feature-gate-arbitrary-self-types.rs:14:18
+error[E0658]: `Ptr<Self>` cannot be used as the type of `self` without the `arbitrary_self_types` feature (see issue #44874)
+ --> $DIR/feature-gate-arbitrary-self-types.rs:26:18
|
-LL | fn foo(self: Rc<Box<Self>>); //~ ERROR arbitrary `self` types are unstable
- | ^^^^^^^^^^^^^
+LL | fn foo(self: Ptr<Self>); //~ ERROR `Ptr<Self>` cannot be used as the type of `self` without
+ | ^^^^^^^^^
|
= help: add #![feature(arbitrary_self_types)] to the crate attributes to enable
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
-error[E0658]: arbitrary `self` types are unstable (see issue #44874)
- --> $DIR/feature-gate-arbitrary-self-types.rs:20:18
+error[E0658]: `Ptr<Bar>` cannot be used as the type of `self` without the `arbitrary_self_types` feature (see issue #44874)
+ --> $DIR/feature-gate-arbitrary-self-types.rs:32:18
|
-LL | fn foo(self: Rc<Box<Self>>) {} //~ ERROR arbitrary `self` types are unstable
- | ^^^^^^^^^^^^^
+LL | fn foo(self: Ptr<Self>) {} //~ ERROR `Ptr<Bar>` cannot be used as the type of `self` without
+ | ^^^^^^^^^
|
= help: add #![feature(arbitrary_self_types)] to the crate attributes to enable
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
-error[E0658]: arbitrary `self` types are unstable (see issue #44874)
- --> $DIR/feature-gate-arbitrary-self-types.rs:24:18
+error[E0658]: `std::boxed::Box<Ptr<Bar>>` cannot be used as the type of `self` without the `arbitrary_self_types` feature (see issue #44874)
+ --> $DIR/feature-gate-arbitrary-self-types.rs:36:18
|
-LL | fn bar(self: Box<Rc<Self>>) {} //~ ERROR arbitrary `self` types are unstable
- | ^^^^^^^^^^^^^
+LL | fn bar(self: Box<Ptr<Self>>) {} //~ ERROR `std::boxed::Box<Ptr<Bar>>` cannot be used as the
+ | ^^^^^^^^^^^^^^
|
= help: add #![feature(arbitrary_self_types)] to the crate attributes to enable
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
impl Foo {
fn foo(self: *const Self) {}
- //~^ ERROR raw pointer `self` is unstable
+ //~^ ERROR `*const Foo` cannot be used as the type of `self` without
}
trait Bar {
fn bar(self: *const Self);
- //~^ ERROR raw pointer `self` is unstable
+ //~^ ERROR `*const Self` cannot be used as the type of `self` without
}
impl Bar for () {
fn bar(self: *const Self) {}
- //~^ ERROR raw pointer `self` is unstable
+ //~^ ERROR `*const ()` cannot be used as the type of `self` without
}
fn main() {}
-error[E0658]: raw pointer `self` is unstable (see issue #44874)
+error[E0658]: `*const Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature (see issue #44874)
--> $DIR/feature-gate-arbitrary_self_types-raw-pointer.rs:19:18
|
LL | fn bar(self: *const Self);
= help: add #![feature(arbitrary_self_types)] to the crate attributes to enable
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
-error[E0658]: raw pointer `self` is unstable (see issue #44874)
+error[E0658]: `*const Foo` cannot be used as the type of `self` without the `arbitrary_self_types` feature (see issue #44874)
--> $DIR/feature-gate-arbitrary_self_types-raw-pointer.rs:14:18
|
LL | fn foo(self: *const Self) {}
= help: add #![feature(arbitrary_self_types)] to the crate attributes to enable
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
-error[E0658]: raw pointer `self` is unstable (see issue #44874)
+error[E0658]: `*const ()` cannot be used as the type of `self` without the `arbitrary_self_types` feature (see issue #44874)
--> $DIR/feature-gate-arbitrary_self_types-raw-pointer.rs:24:18
|
LL | fn bar(self: *const Self) {}
--- /dev/null
+// only-x86_64
+const HUGE_SIZE: usize = !0usize / 8;
+
+
+pub struct TooBigArray {
+ arr: [u8; HUGE_SIZE],
+}
+
+impl TooBigArray {
+ pub const fn new() -> Self {
+ TooBigArray { arr: [0x00; HUGE_SIZE], }
+ }
+}
+
+static MY_TOO_BIG_ARRAY_1: TooBigArray = TooBigArray::new();
+static MY_TOO_BIG_ARRAY_2: [u8; HUGE_SIZE] = [0x00; HUGE_SIZE];
+
+fn main() { }
--- /dev/null
+error: the type `[u8; 2305843009213693951]` is too big for the current architecture
+
+error: aborting due to previous error
+
--- /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.
+
+#![feature(nll)]
+
+struct Foo;
+
+impl Foo {
+ fn get_self(&mut self) -> Option<&mut Self> {
+ Some(self)
+ }
+
+ fn new_self(&mut self) -> &mut Self {
+ self
+ }
+
+ fn trigger_bug(&mut self) {
+ let other = &mut (&mut *self);
+
+ *other = match (*other).get_self() {
+ Some(s) => s,
+ None => (*other).new_self()
+ //~^ ERROR cannot borrow `**other` as mutable more than once at a time [E0499]
+ };
+
+ let c = other;
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0499]: cannot borrow `**other` as mutable more than once at a time
+ --> $DIR/issue-46589.rs:29:21
+ |
+LL | *other = match (*other).get_self() {
+ | -------- first mutable borrow occurs here
+LL | Some(s) => s,
+LL | None => (*other).new_self()
+ | ^^^^^^^^
+ | |
+ | second mutable borrow occurs here
+ | first borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
//~| ERROR (Mir) [E0506]
data.0 = 'f';
//~^ ERROR (Ast) [E0506]
- //~| ERROR (Mir) [E0506]
data.0 = 'g';
//~^ ERROR (Ast) [E0506]
- //~| ERROR (Mir) [E0506]
capitalize(c);
}
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
- --> $DIR/loan_ends_mid_block_pair.rs:31:5
+ --> $DIR/loan_ends_mid_block_pair.rs:30:5
|
LL | let c = &mut data.0;
| ------ borrow of `data.0` occurs here
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
- --> $DIR/loan_ends_mid_block_pair.rs:41:5
+ --> $DIR/loan_ends_mid_block_pair.rs:39:5
|
LL | let c = &mut data.0;
| ------ borrow of `data.0` occurs here
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
- --> $DIR/loan_ends_mid_block_pair.rs:43:5
+ --> $DIR/loan_ends_mid_block_pair.rs:41:5
|
LL | let c = &mut data.0;
| ------ borrow of `data.0` occurs here
| ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
error[E0506]: cannot assign to `data.0` because it is borrowed (Ast)
- --> $DIR/loan_ends_mid_block_pair.rs:45:5
+ --> $DIR/loan_ends_mid_block_pair.rs:43:5
|
LL | let c = &mut data.0;
| ------ borrow of `data.0` occurs here
LL | capitalize(c);
| - borrow later used here
-error[E0506]: cannot assign to `data.0` because it is borrowed (Mir)
- --> $DIR/loan_ends_mid_block_pair.rs:28:5
- |
-LL | let c = &mut data.0;
- | ----------- borrow of `data.0` occurs here
-...
-LL | data.0 = 'f';
- | ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
-...
-LL | capitalize(c);
- | - borrow later used here
-
-error[E0506]: cannot assign to `data.0` because it is borrowed (Mir)
- --> $DIR/loan_ends_mid_block_pair.rs:31:5
- |
-LL | let c = &mut data.0;
- | ----------- borrow of `data.0` occurs here
-...
-LL | data.0 = 'g';
- | ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
-...
-LL | capitalize(c);
- | - borrow later used here
-
-error: aborting due to 9 previous errors
+error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0506`.
#[lang="copy"]
pub trait Copy {}
+#[lang="deref"]
+pub trait Deref {
+ type Target;
+}
+
+#[lang="receiver"]
+pub trait Receiver: Deref {}
+
+impl<'a, T> Deref for &'a T {
+ type Target = T;
+}
+
+impl<'a, T> Receiver for &'a T {}
+
mod bar {
// shouldn't bring in too much
pub use self::glob::*;
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:128:18
+ --> $DIR/privacy1.rs:142:18
|
LL | use bar::baz::{foo, bar};
| ^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:136:18
+ --> $DIR/privacy1.rs:150:18
|
LL | use bar::baz;
| ^^^
error[E0603]: module `i` is private
- --> $DIR/privacy1.rs:160:20
+ --> $DIR/privacy1.rs:174:20
|
LL | use self::foo::i::A; //~ ERROR: module `i` is private
| ^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:100:16
+ --> $DIR/privacy1.rs:114:16
|
LL | ::bar::baz::A::foo(); //~ ERROR: module `baz` is private
| ^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:101:16
+ --> $DIR/privacy1.rs:115:16
|
LL | ::bar::baz::A::bar(); //~ ERROR: module `baz` is private
| ^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:103:16
+ --> $DIR/privacy1.rs:117:16
|
LL | ::bar::baz::A.foo2(); //~ ERROR: module `baz` is private
| ^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:104:16
+ --> $DIR/privacy1.rs:118:16
|
LL | ::bar::baz::A.bar2(); //~ ERROR: module `baz` is private
| ^^^
error[E0603]: trait `B` is private
- --> $DIR/privacy1.rs:108:16
+ --> $DIR/privacy1.rs:122:16
|
LL | ::bar::B::foo(); //~ ERROR: trait `B` is private
| ^
error[E0603]: function `epriv` is private
- --> $DIR/privacy1.rs:114:20
+ --> $DIR/privacy1.rs:128:20
|
LL | ::bar::epriv(); //~ ERROR: function `epriv` is private
| ^^^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:123:16
+ --> $DIR/privacy1.rs:137:16
|
LL | ::bar::baz::foo(); //~ ERROR: module `baz` is private
| ^^^
error[E0603]: module `baz` is private
- --> $DIR/privacy1.rs:124:16
+ --> $DIR/privacy1.rs:138:16
|
LL | ::bar::baz::bar(); //~ ERROR: module `baz` is private
| ^^^
error[E0603]: trait `B` is private
- --> $DIR/privacy1.rs:152:17
+ --> $DIR/privacy1.rs:166:17
|
LL | impl ::bar::B for f32 { fn foo() -> f32 { 1.0 } }
| ^
error[E0624]: method `bar` is private
- --> $DIR/privacy1.rs:73:9
+ --> $DIR/privacy1.rs:87:9
|
LL | self::baz::A::bar(); //~ ERROR: method `bar` is private
| ^^^^^^^^^^^^^^^^^
error[E0624]: method `bar` is private
- --> $DIR/privacy1.rs:91:5
+ --> $DIR/privacy1.rs:105:5
|
LL | bar::A::bar(); //~ ERROR: method `bar` is private
| ^^^^^^^^^^^
error[E0624]: method `bar` is private
- --> $DIR/privacy1.rs:98:9
+ --> $DIR/privacy1.rs:112:9
|
LL | ::bar::A::bar(); //~ ERROR: method `bar` is private
| ^^^^^^^^^^^^^
error[E0624]: method `bar` is private
- --> $DIR/privacy1.rs:101:9
+ --> $DIR/privacy1.rs:115:9
|
LL | ::bar::baz::A::bar(); //~ ERROR: module `baz` is private
| ^^^^^^^^^^^^^^^^^^
error[E0624]: method `bar2` is private
- --> $DIR/privacy1.rs:104:23
+ --> $DIR/privacy1.rs:118:23
|
LL | ::bar::baz::A.bar2(); //~ ERROR: module `baz` is private
| ^^^^
--- /dev/null
+pub type S = u8;
+
+#[macro_export]
+macro_rules! external {
+ () => {
+ dollar_crate::m! {
+ struct M($crate::S);
+ }
+
+ #[dollar_crate::a]
+ struct A($crate::S);
+
+ #[derive(dollar_crate::d)]
+ struct D($crate::S);
+ };
+}
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn m(input: TokenStream) -> TokenStream {
+ println!("PROC MACRO INPUT (PRETTY-PRINTED): {}", input);
+ println!("PROC MACRO INPUT: {:#?}", input);
+ input.into_iter().collect()
+}
+
+#[proc_macro_attribute]
+pub fn a(_args: TokenStream, input: TokenStream) -> TokenStream {
+ println!("ATTRIBUTE INPUT (PRETTY-PRINTED): {}", input);
+ println!("ATTRIBUTE INPUT: {:#?}", input);
+ input.into_iter().collect()
+}
+
+#[proc_macro_derive(d)]
+pub fn d(input: TokenStream) -> TokenStream {
+ println!("DERIVE INPUT (PRETTY-PRINTED): {}", input);
+ println!("DERIVE INPUT: {:#?}", input);
+ input.into_iter().collect()
+}
--- /dev/null
+// edition:2018
+// aux-build:dollar-crate.rs
+// aux-build:dollar-crate-external.rs
+
+// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
+// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
+// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
+
+extern crate dollar_crate;
+extern crate dollar_crate_external;
+
+type S = u8;
+
+mod local {
+ use crate::dollar_crate;
+
+ macro_rules! local {
+ () => {
+ dollar_crate::m! {
+ struct M($crate::S);
+ }
+
+ #[dollar_crate::a]
+ struct A($crate::S);
+
+ #[derive(dollar_crate::d)]
+ struct D($crate::S); //~ ERROR the name `D` is defined multiple times
+ };
+ }
+
+ local!();
+}
+
+mod external {
+ use crate::dollar_crate_external;
+
+ dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
+}
+
+fn main() {}
--- /dev/null
+error[E0428]: the name `D` is defined multiple times
+ --> $DIR/dollar-crate.rs:27:13
+ |
+LL | struct D($crate::S); //~ ERROR the name `D` is defined multiple times
+ | ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | `D` redefined here
+ | previous definition of the type `D` here
+...
+LL | local!();
+ | --------- in this macro invocation
+ |
+ = note: `D` must be defined only once in the type namespace of this module
+
+error[E0428]: the name `D` is defined multiple times
+ --> $DIR/dollar-crate.rs:37:5
+ |
+LL | dollar_crate_external::external!(); //~ ERROR the name `D` is defined multiple times
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | `D` redefined here
+ | previous definition of the type `D` here
+ |
+ = note: `D` must be defined only once in the type namespace of this module
+ = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0428`.
--- /dev/null
+PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ;
+PROC MACRO INPUT: TokenStream [
+ Ident {
+ ident: "struct",
+ span: #2 bytes(LO..HI)
+ },
+ Ident {
+ ident: "M",
+ span: #2 bytes(LO..HI)
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Ident {
+ ident: "$crate",
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Joint,
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: #2 bytes(LO..HI)
+ },
+ Ident {
+ ident: "S",
+ span: #2 bytes(LO..HI)
+ }
+ ],
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #2 bytes(LO..HI)
+ }
+]
+ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(crate::S);
+ATTRIBUTE INPUT: TokenStream [
+ Ident {
+ ident: "struct",
+ span: #2 bytes(LO..HI)
+ },
+ Ident {
+ ident: "A",
+ span: #2 bytes(LO..HI)
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Ident {
+ ident: "$crate",
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Joint,
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: #2 bytes(LO..HI)
+ },
+ Ident {
+ ident: "S",
+ span: #2 bytes(LO..HI)
+ }
+ ],
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #2 bytes(LO..HI)
+ }
+]
+DERIVE INPUT (PRETTY-PRINTED): struct D(crate::S);
+DERIVE INPUT: TokenStream [
+ Ident {
+ ident: "struct",
+ span: #2 bytes(LO..HI)
+ },
+ Ident {
+ ident: "D",
+ span: #2 bytes(LO..HI)
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Ident {
+ ident: "$crate",
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Joint,
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: #2 bytes(LO..HI)
+ },
+ Ident {
+ ident: "S",
+ span: #2 bytes(LO..HI)
+ }
+ ],
+ span: #2 bytes(LO..HI)
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #2 bytes(LO..HI)
+ }
+]
+PROC MACRO INPUT (PRETTY-PRINTED): struct M ( $crate :: S ) ;
+PROC MACRO INPUT: TokenStream [
+ Ident {
+ ident: "struct",
+ span: #10 bytes(LO..HI)
+ },
+ Ident {
+ ident: "M",
+ span: #10 bytes(LO..HI)
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Ident {
+ ident: "$crate",
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Joint,
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: #10 bytes(LO..HI)
+ },
+ Ident {
+ ident: "S",
+ span: #10 bytes(LO..HI)
+ }
+ ],
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #10 bytes(LO..HI)
+ }
+]
+ATTRIBUTE INPUT (PRETTY-PRINTED): struct A(::dollar_crate_external::S);
+ATTRIBUTE INPUT: TokenStream [
+ Ident {
+ ident: "struct",
+ span: #10 bytes(LO..HI)
+ },
+ Ident {
+ ident: "A",
+ span: #10 bytes(LO..HI)
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Ident {
+ ident: "$crate",
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Joint,
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: #10 bytes(LO..HI)
+ },
+ Ident {
+ ident: "S",
+ span: #10 bytes(LO..HI)
+ }
+ ],
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #10 bytes(LO..HI)
+ }
+]
+DERIVE INPUT (PRETTY-PRINTED): struct D(::dollar_crate_external::S);
+DERIVE INPUT: TokenStream [
+ Ident {
+ ident: "struct",
+ span: #10 bytes(LO..HI)
+ },
+ Ident {
+ ident: "D",
+ span: #10 bytes(LO..HI)
+ },
+ Group {
+ delimiter: Parenthesis,
+ stream: TokenStream [
+ Ident {
+ ident: "$crate",
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Joint,
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ':',
+ spacing: Alone,
+ span: #10 bytes(LO..HI)
+ },
+ Ident {
+ ident: "S",
+ span: #10 bytes(LO..HI)
+ }
+ ],
+ span: #10 bytes(LO..HI)
+ },
+ Punct {
+ ch: ';',
+ spacing: Alone,
+ span: #10 bytes(LO..HI)
+ }
+]
struct SomeType {}
trait Foo {
- fn handler(self: &SomeType); //~ ERROR invalid `self` type
+ fn handler(self: &SomeType); //~ ERROR invalid method receiver type
}
fn main() {}
-error[E0307]: invalid `self` type: &SomeType
+error[E0307]: invalid method receiver type: &SomeType
--> $DIR/issue-27522.rs:16:22
|
-LL | fn handler(self: &SomeType); //~ ERROR invalid `self` type
+LL | fn handler(self: &SomeType); //~ ERROR invalid method receiver type
| ^^^^^^^^^
|
= note: type must be `Self` or a type that dereferences to it
impl Foo {
fn foo(self: isize, x: isize) -> isize {
- //~^ ERROR invalid `self` type
+ //~^ ERROR invalid method receiver type
self.f + x
}
}
impl<T> Bar<T> {
fn foo(self: Bar<isize>, x: isize) -> isize {
- //~^ ERROR invalid `self` type
+ //~^ ERROR invalid method receiver type
x
}
fn bar(self: &Bar<usize>, x: isize) -> isize {
- //~^ ERROR invalid `self` type
+ //~^ ERROR invalid method receiver type
x
}
}
-error[E0307]: invalid `self` type: isize
+error[E0307]: invalid method receiver type: isize
--> $DIR/ufcs-explicit-self-bad.rs:18:18
|
LL | fn foo(self: isize, x: isize) -> isize {
| ^^^^^
|
- = note: type must be `Foo` or a type that dereferences to it
+ = note: type must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
-error[E0307]: invalid `self` type: Bar<isize>
+error[E0307]: invalid method receiver type: Bar<isize>
--> $DIR/ufcs-explicit-self-bad.rs:29:18
|
LL | fn foo(self: Bar<isize>, x: isize) -> isize {
| ^^^^^^^^^^
|
- = note: type must be `Bar<T>` or a type that dereferences to it
+ = note: type must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
-error[E0307]: invalid `self` type: &Bar<usize>
+error[E0307]: invalid method receiver type: &Bar<usize>
--> $DIR/ufcs-explicit-self-bad.rs:33:18
|
LL | fn bar(self: &Bar<usize>, x: isize) -> isize {
| ^^^^^^^^^^^
|
- = note: type must be `Bar<T>` or a type that dereferences to it
+ = note: type must be `Self` or a type that dereferences to it
= help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`
error[E0308]: mismatched method receiver
--- /dev/null
+// compile-pass
+
+#![deny(unused_variables)]
+
+mod foo {
+ enum Bar {}
+
+ #[allow(dead_code)]
+ pub struct Foo {
+ value: Bar, // "privately" uninhabited
+ }
+
+ pub fn give_foo() -> Foo { panic!() }
+}
+
+fn main() {
+ let a = 42;
+ foo::give_foo();
+ println!("Hello, {}", a); // ok: we can't tell that this code is dead
+}