[submodule "src/tools/lldb"]
path = src/tools/lldb
url = https://github.com/rust-lang-nursery/lldb/
- branch = rust-release-70
+ branch = rust-release-80-v1
[submodule "src/tools/clang"]
path = src/tools/clang
url = https://github.com/rust-lang-nursery/clang/
- branch = release_70
+ branch = rust-release-80-v1
- env: >
RUST_CHECK_TARGET=dist
- RUST_CONFIGURE_ARGS="--enable-extended --enable-profiler"
+ RUST_CONFIGURE_ARGS="--enable-extended --enable-profiler --enable-lldb"
SRC=.
DEPLOY_ALT=1
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
# OSX 10.7 and `xcode7` is the latest Xcode able to compile LLVM for 10.7.
- env: >
RUST_CHECK_TARGET=dist
- RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-full-tools --enable-profiler"
+ RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-full-tools --enable-profiler --enable-lldb"
SRC=.
DEPLOY=1
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
- env: >
RUST_CHECK_TARGET=dist
- RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler"
+ RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-full-tools --enable-sanitizers --enable-profiler --enable-lldb"
SRC=.
DEPLOY=1
RUSTC_RETRY_LINKER_ON_SEGFAULT=1
llvm_dylib_path.display(), e);
});
- let dst_libdir = image.join("lib");
+ let dst_libdir = image.join("lib/rustlib").join(&*target).join("lib");
t!(fs::create_dir_all(&dst_libdir));
builder.install(&llvm_dylib_path, &dst_libdir, 0o644);
let src_bindir = builder
.llvm_out(target)
.join("bin");
- let dst_bindir = image.join("bin");
+ let dst_bindir = image.join("lib/rustlib")
+ .join(&*target)
+ .join("bin");
t!(fs::create_dir_all(&dst_bindir));
for tool in LLVM_TOOLS {
let exe = src_bindir.join(exe(tool, &target));
}
#[test]
-fn test_trim_left_matches() {
+fn test_trim_start_matches() {
let v: &[char] = &[];
- assert_eq!(" *** foo *** ".trim_left_matches(v), " *** foo *** ");
+ assert_eq!(" *** foo *** ".trim_start_matches(v), " *** foo *** ");
let chars: &[char] = &['*', ' '];
- assert_eq!(" *** foo *** ".trim_left_matches(chars), "foo *** ");
- assert_eq!(" *** *** ".trim_left_matches(chars), "");
- assert_eq!("foo *** ".trim_left_matches(chars), "foo *** ");
+ assert_eq!(" *** foo *** ".trim_start_matches(chars), "foo *** ");
+ assert_eq!(" *** *** ".trim_start_matches(chars), "");
+ assert_eq!("foo *** ".trim_start_matches(chars), "foo *** ");
- assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11");
+ assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11");
let chars: &[char] = &['1', '2'];
- assert_eq!("12foo1bar12".trim_left_matches(chars), "foo1bar12");
- assert_eq!("123foo1bar123".trim_left_matches(|c: char| c.is_numeric()), "foo1bar123");
+ assert_eq!("12foo1bar12".trim_start_matches(chars), "foo1bar12");
+ assert_eq!("123foo1bar123".trim_start_matches(|c: char| c.is_numeric()), "foo1bar123");
}
#[test]
-fn test_trim_right_matches() {
+fn test_trim_end_matches() {
let v: &[char] = &[];
- assert_eq!(" *** foo *** ".trim_right_matches(v), " *** foo *** ");
+ assert_eq!(" *** foo *** ".trim_end_matches(v), " *** foo *** ");
let chars: &[char] = &['*', ' '];
- assert_eq!(" *** foo *** ".trim_right_matches(chars), " *** foo");
- assert_eq!(" *** *** ".trim_right_matches(chars), "");
- assert_eq!(" *** foo".trim_right_matches(chars), " *** foo");
+ assert_eq!(" *** foo *** ".trim_end_matches(chars), " *** foo");
+ assert_eq!(" *** *** ".trim_end_matches(chars), "");
+ assert_eq!(" *** foo".trim_end_matches(chars), " *** foo");
- assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar");
+ assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar");
let chars: &[char] = &['1', '2'];
- assert_eq!("12foo1bar12".trim_right_matches(chars), "12foo1bar");
- assert_eq!("123foo1bar123".trim_right_matches(|c: char| c.is_numeric()), "123foo1bar");
+ assert_eq!("12foo1bar12".trim_end_matches(chars), "12foo1bar");
+ assert_eq!("123foo1bar123".trim_end_matches(|c: char| c.is_numeric()), "123foo1bar");
}
#[test]
}
#[test]
-fn test_trim_left() {
- assert_eq!("".trim_left(), "");
- assert_eq!("a".trim_left(), "a");
- assert_eq!(" ".trim_left(), "");
- assert_eq!(" blah".trim_left(), "blah");
- assert_eq!(" \u{3000} wut".trim_left(), "wut");
- assert_eq!("hey ".trim_left(), "hey ");
+fn test_trim_start() {
+ assert_eq!("".trim_start(), "");
+ assert_eq!("a".trim_start(), "a");
+ assert_eq!(" ".trim_start(), "");
+ assert_eq!(" blah".trim_start(), "blah");
+ assert_eq!(" \u{3000} wut".trim_start(), "wut");
+ assert_eq!("hey ".trim_start(), "hey ");
}
#[test]
-fn test_trim_right() {
- assert_eq!("".trim_right(), "");
- assert_eq!("a".trim_right(), "a");
- assert_eq!(" ".trim_right(), "");
- assert_eq!("blah ".trim_right(), "blah");
- assert_eq!("wut \u{3000} ".trim_right(), "wut");
- assert_eq!(" hey".trim_right(), " hey");
+fn test_trim_end() {
+ assert_eq!("".trim_end(), "");
+ assert_eq!("a".trim_end(), "a");
+ assert_eq!(" ".trim_end(), "");
+ assert_eq!("blah ".trim_end(), "blah");
+ assert_eq!("wut \u{3000} ".trim_end(), "wut");
+ assert_eq!(" hey".trim_end(), " hey");
}
#[test]
"a \t ");
assert_eq!(" \t a \t ".trim_right_matches(|c: char| c.is_whitespace()),
" \t a");
+ assert_eq!(" \t a \t ".trim_start_matches(|c: char| c.is_whitespace()),
+ "a \t ");
+ assert_eq!(" \t a \t ".trim_end_matches(|c: char| c.is_whitespace()),
+ " \t a");
assert_eq!(" \t a \t ".trim_matches(|c: char| c.is_whitespace()),
"a");
assert_eq!(" \t \t ".trim_left_matches(|c: char| c.is_whitespace()),
"");
assert_eq!(" \t \t ".trim_right_matches(|c: char| c.is_whitespace()),
"");
+ assert_eq!(" \t \t ".trim_start_matches(|c: char| c.is_whitespace()),
+ "");
+ assert_eq!(" \t \t ".trim_end_matches(|c: char| c.is_whitespace()),
+ "");
assert_eq!(" \t \t ".trim_matches(|c: char| c.is_whitespace()),
"");
}
-Subproject commit f3a13eb2384c7cbb91b6db5b008377e9710d434c
+Subproject commit 0703bfa72524e01e414477657ca9b64794c5c1c3
/// write!(&mut v, "s = {:?}", s).unwrap(); // uses io::Write::write_fmt
/// assert_eq!(v, b"s = \"abc 123\"");
/// ```
+///
+/// Note: This macro can be used in `no_std` setups as well
+/// In a `no_std` setup you are responsible for the
+/// implementation details of the components.
+///
+/// ```no_run
+/// # extern crate core;
+/// use core::fmt::Write;
+///
+/// struct Example;
+///
+/// impl Write for Example {
+/// fn write_str(&mut self, _s: &str) -> core::fmt::Result {
+/// unimplemented!();
+/// }
+/// }
+///
+/// let mut m = Example{};
+/// write!(&mut m, "Hello World").expect("Not written");
+/// ```
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
macro_rules! write {
self.trim_matches(|c: char| c.is_whitespace())
}
+ /// Returns a string slice with leading whitespace removed.
+ ///
+ /// 'Whitespace' is defined according to the terms of the Unicode Derived
+ /// Core Property `White_Space`.
+ ///
+ /// # Text directionality
+ ///
+ /// A string is a sequence of bytes. `start` in this context means the first
+ /// position of that byte string; for a left-to-right language like English or
+ /// Russian, this will be left side; and for right-to-left languages like
+ /// like Arabic or Hebrew, this will be the right side.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let s = " Hello\tworld\t";
+ /// assert_eq!("Hello\tworld\t", s.trim_start());
+ /// ```
+ ///
+ /// Directionality:
+ ///
+ /// ```
+ /// let s = " English ";
+ /// assert!(Some('E') == s.trim_start().chars().next());
+ ///
+ /// let s = " עברית ";
+ /// assert!(Some('×¢') == s.trim_start().chars().next());
+ /// ```
+ #[stable(feature = "trim_direction", since = "1.30.0")]
+ pub fn trim_start(&self) -> &str {
+ self.trim_start_matches(|c: char| c.is_whitespace())
+ }
+
+ /// Returns a string slice with trailing whitespace removed.
+ ///
+ /// 'Whitespace' is defined according to the terms of the Unicode Derived
+ /// Core Property `White_Space`.
+ ///
+ /// # Text directionality
+ ///
+ /// A string is a sequence of bytes. `end` in this context means the last
+ /// position of that byte string; for a left-to-right language like English or
+ /// Russian, this will be right side; and for right-to-left languages like
+ /// like Arabic or Hebrew, this will be the left side.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// let s = " Hello\tworld\t";
+ /// assert_eq!(" Hello\tworld", s.trim_end());
+ /// ```
+ ///
+ /// Directionality:
+ ///
+ /// ```
+ /// let s = " English ";
+ /// assert!(Some('h') == s.trim_end().chars().rev().next());
+ ///
+ /// let s = " עברית ";
+ /// assert!(Some('ת') == s.trim_end().chars().rev().next());
+ /// ```
+ #[stable(feature = "trim_direction", since = "1.30.0")]
+ pub fn trim_end(&self) -> &str {
+ self.trim_end_matches(|c: char| c.is_whitespace())
+ }
+
/// Returns a string slice with leading whitespace removed.
///
/// 'Whitespace' is defined according to the terms of the Unicode Derived
/// assert!(Some('×¢') == s.trim_left().chars().next());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_deprecated(reason = "superseded by `trim_start`", since = "1.33.0")]
pub fn trim_left(&self) -> &str {
- self.trim_left_matches(|c: char| c.is_whitespace())
+ self.trim_start()
}
/// Returns a string slice with trailing whitespace removed.
/// assert!(Some('ת') == s.trim_right().chars().rev().next());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_deprecated(reason = "superseded by `trim_end`", since = "1.33.0")]
pub fn trim_right(&self) -> &str {
- self.trim_right_matches(|c: char| c.is_whitespace())
+ self.trim_end()
}
/// Returns a string slice with all prefixes and suffixes that match a
/// Basic usage:
///
/// ```
- /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11");
- /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123");
+ /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11");
+ /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123");
///
/// let x: &[_] = &['1', '2'];
- /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12");
+ /// assert_eq!("12foo1bar12".trim_start_matches(x), "foo1bar12");
/// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str {
+ #[stable(feature = "trim_direction", since = "1.30.0")]
+ pub fn trim_start_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str {
let mut i = self.len();
let mut matcher = pat.into_searcher(self);
if let Some((a, _)) = matcher.next_reject() {
/// Simple patterns:
///
/// ```
- /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar");
- /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar");
+ /// assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar");
+ /// assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar");
///
/// let x: &[_] = &['1', '2'];
- /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar");
+ /// assert_eq!("12foo1bar12".trim_end_matches(x), "12foo1bar");
/// ```
///
/// A more complex pattern, using a closure:
///
/// ```
- /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo");
+ /// assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo");
/// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
+ #[stable(feature = "trim_direction", since = "1.30.0")]
+ pub fn trim_end_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
where P::Searcher: ReverseSearcher<'a>
{
let mut j = 0;
}
}
+ /// Returns a string slice with all prefixes that match a pattern
+ /// repeatedly removed.
+ ///
+ /// The pattern can be a `&str`, [`char`], or a closure that determines if
+ /// a character matches.
+ ///
+ /// [`char`]: primitive.char.html
+ ///
+ /// # Text directionality
+ ///
+ /// A string is a sequence of bytes. 'Left' in this context means the first
+ /// position of that byte string; for a language like Arabic or Hebrew
+ /// which are 'right to left' rather than 'left to right', this will be
+ /// the _right_ side, not the left.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11");
+ /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123");
+ ///
+ /// let x: &[_] = &['1', '2'];
+ /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12");
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_deprecated(reason = "superseded by `trim_start_matches`", since = "1.33.0")]
+ pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str {
+ self.trim_start_matches(pat)
+ }
+
+ /// Returns a string slice with all suffixes that match a pattern
+ /// repeatedly removed.
+ ///
+ /// The pattern can be a `&str`, [`char`], or a closure that
+ /// determines if a character matches.
+ ///
+ /// [`char`]: primitive.char.html
+ ///
+ /// # Text directionality
+ ///
+ /// A string is a sequence of bytes. 'Right' in this context means the last
+ /// position of that byte string; for a language like Arabic or Hebrew
+ /// which are 'right to left' rather than 'left to right', this will be
+ /// the _left_ side, not the right.
+ ///
+ /// # Examples
+ ///
+ /// Simple patterns:
+ ///
+ /// ```
+ /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar");
+ /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar");
+ ///
+ /// let x: &[_] = &['1', '2'];
+ /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar");
+ /// ```
+ ///
+ /// A more complex pattern, using a closure:
+ ///
+ /// ```
+ /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo");
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_deprecated(reason = "superseded by `trim_end_matches`", since = "1.33.0")]
+ pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str
+ where P::Searcher: ReverseSearcher<'a>
+ {
+ self.trim_end_matches(pat)
+ }
+
/// Parses this string slice into another type.
///
/// Because `parse` is so general, it can cause problems with type
use ich::{self, CachingSourceMapView, Fingerprint};
use middle::cstore::CrateStore;
use ty::{TyCtxt, fast_reject};
-use mir::interpret::AllocId;
use session::Session;
use std::cmp::Ord;
// CachingSourceMapView, so we initialize it lazily.
raw_source_map: &'a SourceMap,
caching_source_map: Option<CachingSourceMapView<'a>>,
-
- pub(super) alloc_id_recursion_tracker: FxHashSet<AllocId>,
}
#[derive(PartialEq, Eq, Clone, Copy)]
hash_spans: hash_spans_initial,
hash_bodies: true,
node_id_hashing_mode: NodeIdHashingMode::HashDefPath,
- alloc_id_recursion_tracker: Default::default(),
}
}
}
}
-impl_stable_hash_for!(struct ty::VariantDef {
- did,
- name,
- discr,
- fields,
- ctor_kind
-});
+impl<'a> HashStable<StableHashingContext<'a>> for ty::VariantFlags {
+ fn hash_stable<W: StableHasherResult>(&self,
+ _: &mut StableHashingContext<'a>,
+ hasher: &mut StableHasher<W>) {
+ std_hash::Hash::hash(self, hasher);
+ }
+}
impl_stable_hash_for!(enum ty::VariantDiscr {
Explicit(def_id),
ty::tls::with_opt(|tcx| {
trace!("hashing {:?}", *self);
let tcx = tcx.expect("can't hash AllocIds during hir lowering");
- let alloc_kind = tcx.alloc_map.lock().get(*self).expect("no value for AllocId");
+ let alloc_kind = tcx.alloc_map.lock().get(*self);
alloc_kind.hash_stable(hcx, hasher);
});
}
impl<T: layout::HasDataLayout> PointerArithmetic for T {}
+/// Pointer is generic over the type that represents a reference to Allocations,
+/// thus making it possible for the most convenient representation to be used in
+/// each context.
+///
+/// Defaults to the index based and loosely coupled AllocId.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
-pub struct Pointer {
- pub alloc_id: AllocId,
+pub struct Pointer<Id=AllocId> {
+ pub alloc_id: Id,
pub offset: Size,
}
impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)]
-pub struct Relocations(SortedMap<Size, AllocId>);
+pub struct Relocations<Id=AllocId>(SortedMap<Size, Id>);
-impl Relocations {
- pub fn new() -> Relocations {
+impl<Id> Relocations<Id> {
+ pub fn new() -> Self {
Relocations(SortedMap::new())
}
// The caller must guarantee that the given relocations are already sorted
// by address and contain no duplicates.
- pub fn from_presorted(r: Vec<(Size, AllocId)>) -> Relocations {
+ pub fn from_presorted(r: Vec<(Size, Id)>) -> Self {
Relocations(SortedMap::from_presorted_elements(r))
}
}
/// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes
/// of a simple value or a pointer into another `Allocation`
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
-pub enum Scalar {
+pub enum Scalar<Id=AllocId> {
/// The raw bytes of a simple value.
Bits {
/// The first `size` bytes are the value.
/// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of
/// relocations, but a `Scalar` is only large enough to contain one, so we just represent the
/// relocation and its associated offset together as a `Pointer` here.
- Ptr(Pointer),
+ Ptr(Pointer<Id>),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)]
-pub enum ScalarMaybeUndef {
- Scalar(Scalar),
+pub enum ScalarMaybeUndef<Id=AllocId> {
+ Scalar(Scalar<Id>),
Undef,
}
const IS_FUNDAMENTAL = 1 << 2;
const IS_UNION = 1 << 3;
const IS_BOX = 1 << 4;
- /// Indicates whether this abstract data type will be expanded on in future (new
- /// fields/variants) and as such, whether downstream crates must match exhaustively on the
- /// fields/variants of this data type.
- ///
- /// See RFC 2008 (<https://github.com/rust-lang/rfcs/pull/2008>).
- const IS_NON_EXHAUSTIVE = 1 << 5;
+ /// Indicates whether the variant list of this ADT is `#[non_exhaustive]`.
+ /// (i.e., this flag is never set unless this ADT is an enum).
+ const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 5;
+ }
+}
+
+bitflags! {
+ pub struct VariantFlags: u32 {
+ const NO_VARIANT_FLAGS = 0;
+ /// Indicates whether the field list of this variant is `#[non_exhaustive]`.
+ const IS_FIELD_LIST_NON_EXHAUSTIVE = 1 << 0;
}
}
pub discr: VariantDiscr,
pub fields: Vec<FieldDef>,
pub ctor_kind: CtorKind,
+ flags: VariantFlags,
}
+impl<'a, 'gcx, 'tcx> VariantDef {
+ /// Create a new `VariantDef`.
+ ///
+ /// - `did` is the DefId used for the variant - for tuple-structs, it is the constructor DefId,
+ /// and for everything else, it is the variant DefId.
+ /// - `attribute_def_id` is the DefId that has the variant's attributes.
+ pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+ did: DefId,
+ name: Name,
+ discr: VariantDiscr,
+ fields: Vec<FieldDef>,
+ adt_kind: AdtKind,
+ ctor_kind: CtorKind)
+ -> Self
+ {
+ debug!("VariantDef::new({:?}, {:?}, {:?}, {:?}, {:?}, {:?})", did, name, discr, fields,
+ adt_kind, ctor_kind);
+ let mut flags = VariantFlags::NO_VARIANT_FLAGS;
+ if adt_kind == AdtKind::Struct && tcx.has_attr(did, "non_exhaustive") {
+ debug!("found non-exhaustive field list for {:?}", did);
+ flags = flags | VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE;
+ }
+ VariantDef {
+ did,
+ name,
+ discr,
+ fields,
+ ctor_kind,
+ flags
+ }
+ }
+
+ #[inline]
+ pub fn is_field_list_non_exhaustive(&self) -> bool {
+ self.flags.intersects(VariantFlags::IS_FIELD_LIST_NON_EXHAUSTIVE)
+ }
+}
+
+impl_stable_hash_for!(struct VariantDef {
+ did,
+ name,
+ discr,
+ fields,
+ ctor_kind,
+ flags
+});
+
#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum VariantDiscr {
/// Explicit value for this variant, i.e. `X = 123`.
/// Represents the repr options provided by the user,
-#[derive(Copy, Clone, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Default)]
pub struct ReprOptions {
pub int: Option<attr::IntType>,
pub align: u32,
kind: AdtKind,
variants: Vec<VariantDef>,
repr: ReprOptions) -> Self {
+ debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr);
let mut flags = AdtFlags::NO_ADT_FLAGS;
let attrs = tcx.get_attrs(did);
if attr::contains_name(&attrs, "fundamental") {
if Some(did) == tcx.lang_items().owned_box() {
flags = flags | AdtFlags::IS_BOX;
}
- if tcx.has_attr(did, "non_exhaustive") {
- flags = flags | AdtFlags::IS_NON_EXHAUSTIVE;
+ if kind == AdtKind::Enum && tcx.has_attr(did, "non_exhaustive") {
+ debug!("found non-exhaustive variant list for {:?}", did);
+ flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
}
match kind {
AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM,
}
#[inline]
- pub fn is_non_exhaustive(&self) -> bool {
- self.flags.intersects(AdtFlags::IS_NON_EXHAUSTIVE)
+ pub fn is_variant_list_non_exhaustive(&self) -> bool {
+ self.flags.intersects(AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE)
}
/// Returns the kind of the ADT - Struct or Enum.
use libc;
use std::ffi::{CStr, CString};
-use std::fs::{self, File};
+use std::fs;
use std::ptr;
use std::slice;
use std::sync::Arc;
pub enum SerializedModule {
Local(ModuleBuffer),
FromRlib(Vec<u8>),
- FromUncompressedFile(memmap::Mmap, File),
+ FromUncompressedFile(memmap::Mmap),
}
impl SerializedModule {
match *self {
SerializedModule::Local(ref m) => m.data(),
SerializedModule::FromRlib(ref m) => m,
- SerializedModule::FromUncompressedFile(ref m, _) => m,
+ SerializedModule::FromUncompressedFile(ref m) => m,
}
}
}
// Schedule the module to be loaded
drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::AddImportOnlyModule {
- module_data: SerializedModule::FromUncompressedFile(mmap, file),
+ module_data: SerializedModule::FromUncompressedFile(mmap),
work_product: module.source,
})));
}
}
}
+impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
+ where T1: HashStable<CTX>,
+ T2: HashStable<CTX>,
+ T3: HashStable<CTX>,
+ T4: HashStable<CTX>,
+{
+ fn hash_stable<W: StableHasherResult>(&self,
+ ctx: &mut CTX,
+ hasher: &mut StableHasher<W>) {
+ let (ref _0, ref _1, ref _2, ref _3) = *self;
+ _0.hash_stable(ctx, hasher);
+ _1.hash_stable(ctx, hasher);
+ _2.hash_stable(ctx, hasher);
+ _3.hash_stable(ctx, hasher);
+ }
+}
+
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
default fn hash_stable<W: StableHasherResult>(&self,
ctx: &mut CTX,
self.def_path_table.def_path_hash(item_id))
}
- fn get_variant(&self, item: &Entry, index: DefIndex) -> ty::VariantDef {
+ fn get_variant(&self,
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ item: &Entry,
+ index: DefIndex,
+ adt_kind: ty::AdtKind)
+ -> ty::VariantDef
+ {
let data = match item.kind {
EntryKind::Variant(data) |
EntryKind::Struct(data, _) |
_ => bug!(),
};
- ty::VariantDef {
- did: self.local_def_id(data.struct_ctor.unwrap_or(index)),
- name: self.item_name(index).as_symbol(),
- fields: item.children.decode(self).map(|index| {
+ ty::VariantDef::new(
+ tcx,
+ self.local_def_id(data.struct_ctor.unwrap_or(index)),
+ self.item_name(index).as_symbol(),
+ data.discr,
+ item.children.decode(self).map(|index| {
let f = self.entry(index);
ty::FieldDef {
did: self.local_def_id(index),
vis: f.visibility.decode(self)
}
}).collect(),
- discr: data.discr,
- ctor_kind: data.ctor_kind,
- }
+ adt_kind,
+ data.ctor_kind
+ )
}
pub fn get_adt_def(&self,
item.children
.decode(self)
.map(|index| {
- self.get_variant(&self.entry(index), index)
+ self.get_variant(tcx, &self.entry(index), index, kind)
})
.collect()
} else {
- vec![self.get_variant(&item, item_id)]
+ vec![self.get_variant(tcx, &item, item_id, kind)]
};
tcx.alloc_adt_def(did, kind, variants, repr)
// If the structure is marked as non_exhaustive then lower the visibility
// to within the crate.
- if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
+ if adt_def.non_enum_variant().is_field_list_non_exhaustive() &&
+ ctor_vis == ty::Visibility::Public
+ {
ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
}
flow_inits,
move_data,
elements,
- errors_buffer,
);
if let Some(all_facts) = &mut all_facts {
use rustc::traits::query::{Fallible, NoSolution};
use rustc::ty::fold::TypeFoldable;
use rustc::ty::{self, CanonicalTy, RegionVid, ToPolyTraitRef, Ty, TyCtxt, TyKind};
-use rustc_errors::Diagnostic;
use std::fmt;
use std::rc::Rc;
use syntax_pos::{Span, DUMMY_SP};
/// - `liveness` -- results of a liveness computation on the MIR; used to create liveness
/// constraints for the regions in the types of variables
/// - `flow_inits` -- results of a maybe-init dataflow analysis
-/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysis
-/// - `errors_buffer` -- errors are sent here for future reporting
+/// - `move_data` -- move-data constructed when performing the maybe-init dataflow analysiss
pub(crate) fn type_check<'gcx, 'tcx>(
infcx: &InferCtxt<'_, 'gcx, 'tcx>,
param_env: ty::ParamEnv<'gcx>,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
elements: &Rc<RegionValueElements>,
- errors_buffer: &mut Vec<Diagnostic>,
) -> MirTypeckResults<'tcx> {
let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
let mut constraints = MirTypeckRegionConstraints {
®ion_bound_pairs,
Some(implicit_region_bound),
Some(&mut borrowck_context),
- Some(errors_buffer),
|cx| {
cx.equate_inputs_and_outputs(
mir,
region_bound_pairs: &'a [(ty::Region<'tcx>, GenericKind<'tcx>)],
implicit_region_bound: Option<ty::Region<'tcx>>,
borrowck_context: Option<&'a mut BorrowCheckContext<'a, 'tcx>>,
- errors_buffer: Option<&mut Vec<Diagnostic>>,
mut extra: impl FnMut(&mut TypeChecker<'a, 'gcx, 'tcx>) -> R,
) -> R where {
let mut checker = TypeChecker::new(
if !errors_reported {
// if verifier failed, don't do further checks to avoid ICEs
- checker.typeck_mir(mir, errors_buffer);
+ checker.typeck_mir(mir);
}
extra(&mut checker)
mir: &Mir<'tcx>,
term: &Terminator<'tcx>,
term_location: Location,
- errors_buffer: &mut Option<&mut Vec<Diagnostic>>,
) {
debug!("check_terminator: {:?}", term);
let tcx = self.tcx();
&sig,
);
let sig = self.normalize(sig, term_location);
- self.check_call_dest(mir, term, &sig, destination, term_location, errors_buffer);
+ self.check_call_dest(mir, term, &sig, destination, term_location);
self.prove_predicates(
sig.inputs().iter().map(|ty| ty::Predicate::WellFormed(ty)),
sig: &ty::FnSig<'tcx>,
destination: &Option<(Place<'tcx>, BasicBlock)>,
term_location: Location,
- errors_buffer: &mut Option<&mut Vec<Diagnostic>>,
) {
let tcx = self.tcx();
match *destination {
// this check is done at `check_local`.
if self.tcx().features().unsized_locals {
let span = term.source_info.span;
- self.ensure_place_sized(dest_ty, span, errors_buffer);
+ self.ensure_place_sized(dest_ty, span);
}
}
None => {
mir: &Mir<'tcx>,
local: Local,
local_decl: &LocalDecl<'tcx>,
- errors_buffer: &mut Option<&mut Vec<Diagnostic>>,
) {
match mir.local_kind(local) {
LocalKind::ReturnPointer | LocalKind::Arg => {
}
// When `#![feature(unsized_locals)]` is enabled, only function calls
- // are checked in `check_call_dest`.
+ // and nullary ops are checked in `check_call_dest`.
if !self.tcx().features().unsized_locals {
let span = local_decl.source_info.span;
let ty = local_decl.ty;
- self.ensure_place_sized(ty, span, errors_buffer);
+ self.ensure_place_sized(ty, span);
}
}
- fn ensure_place_sized(&mut self,
- ty: Ty<'tcx>,
- span: Span,
- errors_buffer: &mut Option<&mut Vec<Diagnostic>>) {
+ fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
let tcx = self.tcx();
// Erase the regions from `ty` to get a global type. The
cannot be statically determined",
ty
);
- if let Some(ref mut errors_buffer) = *errors_buffer {
- diag.buffer(errors_buffer);
- } else {
- // we're allowed to use emit() here because the
- // NLL migration will be turned on (and thus
- // errors will need to be buffered) *only if*
- // errors_buffer is Some.
- diag.emit();
- }
+
+ // While this is located in `nll::typeck` this error is not
+ // an NLL error, it's a required check to prevent creation
+ // of unsized rvalues in certain cases:
+ // * operand of a box expression
+ // * callee in a call expression
+ diag.emit();
}
}
}
},
Rvalue::NullaryOp(_, ty) => {
+ // Even with unsized locals cannot box an unsized value.
+ if self.tcx().features().unsized_locals {
+ let span = mir.source_info(location).span;
+ self.ensure_place_sized(ty, span);
+ }
+
let trait_ref = ty::TraitRef {
def_id: tcx.lang_items().sized_trait().unwrap(),
substs: tcx.mk_substs_trait(ty, &[]),
})
}
- fn typeck_mir(&mut self, mir: &Mir<'tcx>, mut errors_buffer: Option<&mut Vec<Diagnostic>>) {
+ fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
self.last_span = mir.span;
debug!("run_on_mir: {:?}", mir.span);
for (local, local_decl) in mir.local_decls.iter_enumerated() {
- self.check_local(mir, local, local_decl, &mut errors_buffer);
+ self.check_local(mir, local, local_decl);
}
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
location.statement_index += 1;
}
- self.check_terminator(mir, block_data.terminator(), location, &mut errors_buffer);
+ self.check_terminator(mir, block_data.terminator(), location);
self.check_iscleanup(mir, block_data);
}
}
&[],
None,
None,
- None,
|_| (),
);
}
}
+impl_stable_hash_for!(struct CompileTimeEvaluator {});
+
#[derive(Clone, Debug)]
enum ConstEvalError {
NeedsRfc(String),
fn is_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
match ty.sty {
- ty::Adt(adt_def, ..) => adt_def.is_enum() && adt_def.is_non_exhaustive(),
+ ty::Adt(adt_def, ..) => adt_def.is_variant_list_non_exhaustive(),
_ => false,
}
}
// except according to those terms.
use std::fmt::Write;
-use std::hash::{Hash, Hasher};
use std::mem;
use rustc::hir::def_id::DefId;
use rustc::hir::def::Def;
use rustc::hir::map::definitions::DefPathData;
+use rustc::ich::StableHashingContext;
use rustc::mir;
use rustc::ty::layout::{
self, Size, Align, HasDataLayout, LayoutOf, TyLayout
use rustc::ty::subst::{Subst, Substs};
use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::ty::query::TyCtxtAt;
-use rustc_data_structures::fx::{FxHashSet, FxHasher};
use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use rustc::mir::interpret::{
- GlobalId, Scalar, FrameInfo,
+ GlobalId, Scalar, FrameInfo, AllocId,
EvalResult, EvalErrorKind,
ScalarMaybeUndef,
truncate, sign_extend,
Memory, Machine
};
+use super::snapshot::InfiniteLoopDetector;
+
pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
/// Stores the `Machine` instance.
pub machine: M,
/// The locals are stored as `Option<Value>`s.
/// `None` represents a local that is currently dead, while a live local
/// can either directly contain `Scalar` or refer to some part of an `Allocation`.
- pub locals: IndexVec<mir::Local, LocalValue>,
+ pub locals: IndexVec<mir::Local, LocalValue<AllocId>>,
////////////////////////////////////////////////////////////////////////////////
// Current position within the function
pub stmt: usize,
}
-impl<'mir, 'tcx: 'mir> Eq for Frame<'mir, 'tcx> {}
-
-impl<'mir, 'tcx: 'mir> PartialEq for Frame<'mir, 'tcx> {
- fn eq(&self, other: &Self) -> bool {
- let Frame {
- mir: _,
- instance,
- span: _,
- return_to_block,
- return_place,
- locals,
- block,
- stmt,
- } = self;
-
- // Some of these are constant during evaluation, but are included
- // anyways for correctness.
- *instance == other.instance
- && *return_to_block == other.return_to_block
- && *return_place == other.return_place
- && *locals == other.locals
- && *block == other.block
- && *stmt == other.stmt
- }
-}
+impl<'a, 'mir, 'tcx: 'mir> HashStable<StableHashingContext<'a>> for Frame<'mir, 'tcx> {
+ fn hash_stable<W: StableHasherResult>(
+ &self,
+ hcx: &mut StableHashingContext<'a>,
+ hasher: &mut StableHasher<W>) {
-impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> {
- fn hash<H: Hasher>(&self, state: &mut H) {
let Frame {
- mir: _,
+ mir,
instance,
- span: _,
+ span,
return_to_block,
return_place,
locals,
stmt,
} = self;
- instance.hash(state);
- return_to_block.hash(state);
- return_place.hash(state);
- locals.hash(state);
- block.hash(state);
- stmt.hash(state);
+ (mir, instance, span, return_to_block).hash_stable(hcx, hasher);
+ (return_place, locals, block, stmt).hash_stable(hcx, hasher);
}
}
None { cleanup: bool },
}
+impl<'a> HashStable<StableHashingContext<'a>> for StackPopCleanup {
+ fn hash_stable<W: StableHasherResult>(
+ &self,
+ hcx: &mut StableHashingContext<'a>,
+ hasher: &mut StableHasher<W>) {
+ match self {
+ StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher),
+ StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher),
+ }
+ }
+}
+
// State of a local variable
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub enum LocalValue {
+pub enum LocalValue<Id=AllocId> {
Dead,
// Mostly for convenience, we re-use the `Operand` type here.
// This is an optimization over just always having a pointer here;
// we can thus avoid doing an allocation when the local just stores
// immediate values *and* never has its address taken.
- Live(Operand),
+ Live(Operand<Id>),
}
impl<'tcx> LocalValue {
}
}
-/// The virtual machine state during const-evaluation at a given point in time.
-type EvalSnapshot<'a, 'mir, 'tcx, M>
- = (M, Vec<Frame<'mir, 'tcx>>, Memory<'a, 'mir, 'tcx, M>);
-
-pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
- /// The set of all `EvalSnapshot` *hashes* observed by this detector.
- ///
- /// When a collision occurs in this table, we store the full snapshot in
- /// `snapshots`.
- hashes: FxHashSet<u64>,
-
- /// The set of all `EvalSnapshot`s observed by this detector.
- ///
- /// An `EvalSnapshot` will only be fully cloned once it has caused a
- /// collision in `hashes`. As a result, the detector must observe at least
- /// *two* full cycles of an infinite loop before it triggers.
- snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx, M>>,
-}
-
-impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- fn default() -> Self {
- InfiniteLoopDetector {
- hashes: FxHashSet::default(),
- snapshots: FxHashSet::default(),
- }
- }
-}
-
-impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- /// Returns `true` if the loop detector has not yet observed a snapshot.
- pub fn is_empty(&self) -> bool {
- self.hashes.is_empty()
- }
-
- pub fn observe_and_analyze(
- &mut self,
- machine: &M,
- stack: &Vec<Frame<'mir, 'tcx>>,
- memory: &Memory<'a, 'mir, 'tcx, M>,
- ) -> EvalResult<'tcx, ()> {
- let snapshot = (machine, stack, memory);
-
- let mut fx = FxHasher::default();
- snapshot.hash(&mut fx);
- let hash = fx.finish();
-
- if self.hashes.insert(hash) {
- // No collision
- return Ok(())
- }
-
- if self.snapshots.insert((machine.clone(), stack.clone(), memory.clone())) {
- // Spurious collision or first cycle
- return Ok(())
- }
-
- // Second cycle
- Err(EvalErrorKind::InfiniteLoop.into())
- }
-}
+impl_stable_hash_for!(enum self::LocalValue {
+ Dead,
+ Live(x),
+});
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> {
#[inline]
use std::hash::Hash;
use rustc::hir::def_id::DefId;
+use rustc::ich::StableHashingContext;
use rustc::mir::interpret::{Allocation, EvalResult, Scalar};
use rustc::mir;
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
+use rustc_data_structures::stable_hasher::HashStable;
use super::{EvalContext, PlaceTy, OpTy};
/// Methods of this trait signifies a point where CTFE evaluation would fail
/// and some use case dependent behaviour can instead be applied
-pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash {
+pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash + for<'a> HashStable<StableHashingContext<'a>> {
/// Additional data that can be accessed via the Memory
- type MemoryData: Clone + Eq + Hash;
+ type MemoryData: Clone + Eq + Hash + for<'a> HashStable<StableHashingContext<'a>>;
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq + Hash;
//! short-circuiting the empty case!
use std::collections::VecDeque;
-use std::hash::{Hash, Hasher};
use std::ptr;
use rustc::ty::{self, Instance, query::TyCtxtAt};
EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
truncate};
pub use rustc::mir::interpret::{write_target_uint, read_target_uint};
-use rustc_data_structures::fx::{FxHashSet, FxHashMap, FxHasher};
+use rustc_data_structures::fx::{FxHashSet, FxHashMap};
use syntax::ast::Mutability;
}
}
-impl<'a, 'mir, 'tcx, M> Eq for Memory<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{}
-
-impl<'a, 'mir, 'tcx, M> PartialEq for Memory<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- fn eq(&self, other: &Self) -> bool {
- let Memory {
- data,
- alloc_map,
- tcx: _,
- } = self;
-
- *data == other.data
- && *alloc_map == other.alloc_map
- }
-}
-
-impl<'a, 'mir, 'tcx, M> Hash for Memory<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- fn hash<H: Hasher>(&self, state: &mut H) {
- let Memory {
- data,
- alloc_map: _,
- tcx: _,
- } = self;
-
- data.hash(state);
-
- // We ignore some fields which don't change between evaluation steps.
-
- // Since HashMaps which contain the same items may have different
- // iteration orders, we use a commutative operation (in this case
- // addition, but XOR would also work), to combine the hash of each
- // `Allocation`.
- self.alloc_map.iter()
- .map(|(&id, alloc)| {
- let mut h = FxHasher::default();
- id.hash(&mut h);
- alloc.hash(&mut h);
- h.finish()
- })
- .fold(0u64, |hash, x| hash.wrapping_add(x))
- .hash(state);
- }
-}
-
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
Memory {
mod machine;
mod memory;
mod operator;
+mod snapshot;
mod step;
mod terminator;
mod traits;
use rustc::{mir, ty};
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt};
use rustc_data_structures::indexed_vec::Idx;
-
use rustc::mir::interpret::{
- GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind
+ GlobalId, AllocId,
+ ConstValue, Pointer, Scalar, ScalarMaybeUndef,
+ EvalResult, EvalErrorKind
};
use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind};
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
/// defined on `Value`, and do not have to work with a `Place`.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub enum Value {
- Scalar(ScalarMaybeUndef),
- ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef),
+pub enum Value<Id=AllocId> {
+ Scalar(ScalarMaybeUndef<Id>),
+ ScalarPair(ScalarMaybeUndef<Id>, ScalarMaybeUndef<Id>),
}
impl<'tcx> Value {
}
}
+impl_stable_hash_for!(enum ::interpret::Value {
+ Scalar(x),
+ ScalarPair(x, y),
+});
+
// ScalarPair needs a type to interpret, so we often have a value and a type together
// as input for binary and cast operations.
#[derive(Copy, Clone, Debug)]
/// or still in memory. The latter is an optimization, to delay reading that chunk of
/// memory and to avoid having to store arbitrary-sized data here.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub enum Operand {
- Immediate(Value),
- Indirect(MemPlace),
+pub enum Operand<Id=AllocId> {
+ Immediate(Value<Id>),
+ Indirect(MemPlace<Id>),
}
impl Operand {
}
}
+impl_stable_hash_for!(enum ::interpret::Operand {
+ Immediate(x),
+ Indirect(x),
+});
+
#[derive(Copy, Clone, Debug)]
pub struct OpTy<'tcx> {
crate op: Operand, // ideally we'd make this private, but const_prop needs this
use std::convert::TryFrom;
+use rustc::ich::StableHashingContext;
use rustc::mir;
use rustc::ty::{self, Ty};
use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout};
use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
use rustc::mir::interpret::{
- GlobalId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic
+ GlobalId, AllocId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic
};
use super::{EvalContext, Machine, Value, ValTy, Operand, OpTy, MemoryKind};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub struct MemPlace {
+pub struct MemPlace<Id=AllocId> {
/// A place may have an integral pointer for ZSTs, and since it might
/// be turned back into a reference before ever being dereferenced.
/// However, it may never be undef.
- pub ptr: Scalar,
+ pub ptr: Scalar<Id>,
pub align: Align,
/// Metadata for unsized places. Interpretation is up to the type.
/// Must not be present for sized types, but can be missing for unsized types
/// (e.g. `extern type`).
- pub extra: Option<Scalar>,
+ pub extra: Option<Scalar<Id>>,
}
+impl_stable_hash_for!(struct ::interpret::MemPlace {
+ ptr,
+ align,
+ extra,
+});
+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub enum Place {
+pub enum Place<Id=AllocId> {
/// A place referring to a value allocated in the `Memory` system.
- Ptr(MemPlace),
+ Ptr(MemPlace<Id>),
/// To support alloc-free locals, we are able to write directly to a local.
/// (Without that optimization, we'd just always be a `MemPlace`.)
},
}
+impl<'a> HashStable<StableHashingContext<'a>> for Place {
+ fn hash_stable<W: StableHasherResult>(
+ &self, hcx: &mut StableHashingContext<'a>,
+ hasher: &mut StableHasher<W>) {
+
+ match self {
+ Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher),
+
+ Place::Local { frame, local } => {
+ frame.hash_stable(hcx, hasher);
+ local.hash_stable(hcx, hasher);
+ },
+ }
+ }
+}
#[derive(Copy, Clone, Debug)]
pub struct PlaceTy<'tcx> {
place: Place,
--- /dev/null
+//! This module contains the machinery necessary to detect infinite loops
+//! during const-evaluation by taking snapshots of the state of the interpreter
+//! at regular intervals.
+
+use std::hash::{Hash, Hasher};
+
+use rustc::ich::{StableHashingContext, StableHashingContextProvider};
+use rustc::mir;
+use rustc::mir::interpret::{
+ AllocId, Pointer, Scalar, ScalarMaybeUndef,
+ Relocations, Allocation, UndefMask,
+ EvalResult, EvalErrorKind,
+};
+
+use rustc::ty::{self, TyCtxt};
+use rustc::ty::layout::Align;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
+use syntax::ast::Mutability;
+use syntax::source_map::Span;
+
+use super::eval_context::{LocalValue, StackPopCleanup};
+use super::{Frame, Memory, Machine, Operand, MemPlace, Place, Value};
+
+pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
+ /// The set of all `EvalSnapshot` *hashes* observed by this detector.
+ ///
+ /// When a collision occurs in this table, we store the full snapshot in
+ /// `snapshots`.
+ hashes: FxHashSet<u64>,
+
+ /// The set of all `EvalSnapshot`s observed by this detector.
+ ///
+ /// An `EvalSnapshot` will only be fully cloned once it has caused a
+ /// collision in `hashes`. As a result, the detector must observe at least
+ /// *two* full cycles of an infinite loop before it triggers.
+ snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx, M>>,
+}
+
+impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+ 'tcx: 'a + 'mir,
+{
+ fn default() -> Self {
+ InfiniteLoopDetector {
+ hashes: FxHashSet::default(),
+ snapshots: FxHashSet::default(),
+ }
+ }
+}
+
+impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+ 'tcx: 'a + 'mir,
+{
+ /// Returns `true` if the loop detector has not yet observed a snapshot.
+ pub fn is_empty(&self) -> bool {
+ self.hashes.is_empty()
+ }
+
+ pub fn observe_and_analyze(
+ &mut self,
+ tcx: &TyCtxt<'b, 'tcx, 'tcx>,
+ machine: &M,
+ memory: &Memory<'a, 'mir, 'tcx, M>,
+ stack: &[Frame<'mir, 'tcx>],
+ ) -> EvalResult<'tcx, ()> {
+
+ let mut hcx = tcx.get_stable_hashing_context();
+ let mut hasher = StableHasher::<u64>::new();
+ (machine, stack).hash_stable(&mut hcx, &mut hasher);
+ let hash = hasher.finish();
+
+ if self.hashes.insert(hash) {
+ // No collision
+ return Ok(())
+ }
+
+ info!("snapshotting the state of the interpreter");
+
+ if self.snapshots.insert(EvalSnapshot::new(machine, memory, stack)) {
+ // Spurious collision or first cycle
+ return Ok(())
+ }
+
+ // Second cycle
+ Err(EvalErrorKind::InfiniteLoop.into())
+ }
+}
+
+trait SnapshotContext<'a> {
+ fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>;
+}
+
+/// Taking a snapshot of the evaluation context produces a view of
+/// the state of the interpreter that is invariant to `AllocId`s.
+trait Snapshot<'a, Ctx: SnapshotContext<'a>> {
+ type Item;
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item;
+}
+
+macro_rules! __impl_snapshot_field {
+ ($field:ident, $ctx:expr) => ($field.snapshot($ctx));
+ ($field:ident, $ctx:expr, $delegate:expr) => ($delegate);
+}
+
+macro_rules! impl_snapshot_for {
+ // FIXME(mark-i-m): Some of these should be `?` rather than `*`.
+ (enum $enum_name:ident {
+ $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)*),* ) )* ),* $(,)*
+ }) => {
+
+ impl<'a, Ctx> self::Snapshot<'a, Ctx> for $enum_name
+ where Ctx: self::SnapshotContext<'a>,
+ {
+ type Item = $enum_name<AllocIdSnapshot<'a>>;
+
+ #[inline]
+ fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item {
+ match *self {
+ $(
+ $enum_name::$variant $( ( $(ref $field),* ) )* =>
+ $enum_name::$variant $(
+ ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* ),
+ )*
+ )*
+ }
+ }
+ }
+ };
+
+ // FIXME(mark-i-m): same here.
+ (struct $struct_name:ident { $($field:ident $(-> $delegate:expr)*),* $(,)* }) => {
+ impl<'a, Ctx> self::Snapshot<'a, Ctx> for $struct_name
+ where Ctx: self::SnapshotContext<'a>,
+ {
+ type Item = $struct_name<AllocIdSnapshot<'a>>;
+
+ #[inline]
+ fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item {
+ let $struct_name {
+ $(ref $field),*
+ } = *self;
+
+ $struct_name {
+ $( $field: __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),*
+ }
+ }
+ }
+ };
+}
+
+impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option<T>
+ where Ctx: SnapshotContext<'a>,
+ T: Snapshot<'a, Ctx>
+{
+ type Item = Option<<T as Snapshot<'a, Ctx>>::Item>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ match self {
+ Some(x) => Some(x.snapshot(ctx)),
+ None => None,
+ }
+ }
+}
+
+#[derive(Eq, PartialEq)]
+struct AllocIdSnapshot<'a>(Option<AllocationSnapshot<'a>>);
+
+impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = AllocIdSnapshot<'a>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ AllocIdSnapshot(ctx.resolve(self).map(|alloc| alloc.snapshot(ctx)))
+ }
+}
+
+impl_snapshot_for!(struct Pointer {
+ alloc_id,
+ offset -> *offset,
+});
+
+impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = Scalar<AllocIdSnapshot<'a>>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ match self {
+ Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)),
+ Scalar::Bits{ size, bits } => Scalar::Bits {
+ size: *size,
+ bits: *bits,
+ },
+ }
+ }
+}
+
+impl_snapshot_for!(enum ScalarMaybeUndef {
+ Scalar(s),
+ Undef,
+});
+
+impl_snapshot_for!(struct MemPlace {
+ ptr,
+ extra,
+ align -> *align,
+});
+
+impl<'a, Ctx> Snapshot<'a, Ctx> for Place
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = Place<AllocIdSnapshot<'a>>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ match self {
+ Place::Ptr(p) => Place::Ptr(p.snapshot(ctx)),
+
+ Place::Local{ frame, local } => Place::Local{
+ frame: *frame,
+ local: *local,
+ },
+ }
+ }
+}
+
+impl_snapshot_for!(enum Value {
+ Scalar(s),
+ ScalarPair(s, t),
+});
+
+impl_snapshot_for!(enum Operand {
+ Immediate(v),
+ Indirect(m),
+});
+
+impl_snapshot_for!(enum LocalValue {
+ Live(v),
+ Dead,
+});
+
+impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = Relocations<AllocIdSnapshot<'a>>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ Relocations::from_presorted(self.iter()
+ .map(|(size, id)| (*size, id.snapshot(ctx)))
+ .collect())
+ }
+}
+
+#[derive(Eq, PartialEq)]
+struct AllocationSnapshot<'a> {
+ bytes: &'a [u8],
+ relocations: Relocations<AllocIdSnapshot<'a>>,
+ undef_mask: &'a UndefMask,
+ align: &'a Align,
+ mutability: &'a Mutability,
+}
+
+impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = AllocationSnapshot<'a>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ let Allocation { bytes, relocations, undef_mask, align, mutability } = self;
+
+ AllocationSnapshot {
+ bytes,
+ undef_mask,
+ align,
+ mutability,
+ relocations: relocations.snapshot(ctx),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq)]
+struct FrameSnapshot<'a, 'tcx: 'a> {
+ instance: &'a ty::Instance<'tcx>,
+ span: &'a Span,
+ return_to_block: &'a StackPopCleanup,
+ return_place: Place<AllocIdSnapshot<'a>>,
+ locals: IndexVec<mir::Local, LocalValue<AllocIdSnapshot<'a>>>,
+ block: &'a mir::BasicBlock,
+ stmt: usize,
+}
+
+impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
+ where Ctx: SnapshotContext<'a>,
+{
+ type Item = FrameSnapshot<'a, 'tcx>;
+
+ fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
+ let Frame {
+ mir: _,
+ instance,
+ span,
+ return_to_block,
+ return_place,
+ locals,
+ block,
+ stmt,
+ } = self;
+
+ FrameSnapshot {
+ instance,
+ span,
+ return_to_block,
+ block,
+ stmt: *stmt,
+ return_place: return_place.snapshot(ctx),
+ locals: locals.iter().map(|local| local.snapshot(ctx)).collect(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq)]
+struct MemorySnapshot<'a, 'mir: 'a, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx> + 'a> {
+ data: &'a M::MemoryData,
+}
+
+impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{
+ fn snapshot<'b: 'a>(&'b self) -> MemorySnapshot<'b, 'mir, 'tcx, M> {
+ let Memory { data, .. } = self;
+ MemorySnapshot { data }
+ }
+}
+
+impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{
+ fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> {
+ self.get(*id).ok()
+ }
+}
+
+/// The virtual machine state during const-evaluation at a given point in time.
+struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
+ machine: M,
+ memory: Memory<'a, 'mir, 'tcx, M>,
+ stack: Vec<Frame<'mir, 'tcx>>,
+}
+
+impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{
+ fn new(
+ machine: &M,
+ memory: &Memory<'a, 'mir, 'tcx, M>,
+ stack: &[Frame<'mir, 'tcx>]) -> Self {
+
+ EvalSnapshot {
+ machine: machine.clone(),
+ memory: memory.clone(),
+ stack: stack.into(),
+ }
+ }
+
+ fn snapshot<'b: 'a>(&'b self)
+ -> (&'b M, MemorySnapshot<'b, 'mir, 'tcx, M>, Vec<FrameSnapshot<'a, 'tcx>>) {
+ let EvalSnapshot{ machine, memory, stack } = self;
+ (&machine, memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect())
+ }
+}
+
+impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2)
+ let mut hcx = self.memory.tcx.get_stable_hashing_context();
+ let mut hasher = StableHasher::<u64>::new();
+ self.hash_stable(&mut hcx, &mut hasher);
+ hasher.finish().hash(state)
+ }
+}
+
+impl<'a, 'b, 'mir, 'tcx, M> HashStable<StableHashingContext<'b>>
+ for EvalSnapshot<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{
+ fn hash_stable<W: StableHasherResult>(
+ &self,
+ hcx: &mut StableHashingContext<'b>,
+ hasher: &mut StableHasher<W>) {
+
+ let EvalSnapshot{ machine, memory, stack } = self;
+ (machine, &memory.data, stack).hash_stable(hcx, hasher);
+ }
+}
+
+impl<'a, 'mir, 'tcx, M> Eq for EvalSnapshot<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{}
+
+impl<'a, 'mir, 'tcx, M> PartialEq for EvalSnapshot<'a, 'mir, 'tcx, M>
+ where M: Machine<'mir, 'tcx>,
+{
+ fn eq(&self, other: &Self) -> bool {
+ self.snapshot() == other.snapshot()
+ }
+}
"Constant evaluating a complex constant, this might take some time");
}
- self.loop_detector.observe_and_analyze(&self.machine, &self.stack, &self.memory)
+ self.loop_detector.observe_and_analyze(
+ &self.tcx,
+ &self.machine,
+ &self.memory,
+ &self.stack[..],
+ )
}
pub fn run(&mut self) -> EvalResult<'tcx> {
// visibility to within the crate.
let struct_def_id = self.tcx.hir.get_parent_did(node_id);
let adt_def = self.tcx.adt_def(struct_def_id);
- if adt_def.is_non_exhaustive() && ctor_vis == ty::Visibility::Public {
+ if adt_def.non_enum_variant().is_field_list_non_exhaustive()
+ && ctor_vis == ty::Visibility::Public
+ {
ctor_vis = ty::Visibility::Restricted(
DefId::local(CRATE_DEF_INDEX));
}
self.session.features_untracked().uniform_paths);
let source = module_path[0];
+
+ // HACK(eddyb) For `use x::{self, ...};`, use the ID of the
+ // `self` nested import for the canary. This allows the
+ // ambiguity reporting scope to ignore false positives
+ // in the same way it does for `use x;` (by comparing IDs).
+ let mut canary_id = id;
+ if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
+ for &(ref use_tree, id) in items {
+ if let ast::UseTreeKind::Simple(..) = use_tree.kind {
+ if use_tree.ident().name == keywords::SelfValue.name() {
+ canary_id = id;
+ break;
+ }
+ }
+ }
+ }
+
// Helper closure to emit a canary with the given base path.
let emit = |this: &mut Self, base: Option<Ident>| {
let subclass = SingleImport {
base.into_iter().collect(),
subclass.clone(),
source.span,
- id,
+ canary_id,
root_use_tree.span,
root_id,
ty::Visibility::Invisible,
self.per_ns(|_, ns| {
if let Some(result) = result[ns].get().ok() {
+ if let NameBindingKind::Import { directive, .. } = result.kind {
+ // Skip canaries that resolve to the import itself.
+ // These come from `use crate_name;`, which isn't really
+ // ambiguous, as the import can't actually shadow itself.
+ if directive.id == import.id {
+ return;
+ }
+ }
if has_explicit_self {
// There should only be one `self::x` (module-scoped) canary.
assert_eq!(canary_results[ns].module_scope, None);
errors = true;
- // Special-case the error when `self::x` finds its own `use x;`.
- if has_external_crate &&
- results.module_scope == Some(span) &&
- results.block_scopes.is_empty() {
- let msg = format!("`{}` import is redundant", name);
- this.session.struct_span_err(span, &msg)
- .span_label(span,
- format!("refers to external crate `::{}`", name))
- .span_label(span,
- format!("defines `self::{}`, shadowing itself", name))
- .help(&format!("remove or write `::{}` explicitly instead", name))
- .note("relative `use` paths enabled by `#![feature(uniform_paths)]`")
- .emit();
- return;
- }
-
let msg = format!("`{}` import is ambiguous", name);
let mut err = this.session.struct_span_err(span, &msg);
let mut suggestion_choices = String::new();
}
// Require `..` if struct has non_exhaustive attribute.
- if adt.is_struct() && adt.is_non_exhaustive() && !adt.did.is_local() && !etc {
+ if variant.is_field_list_non_exhaustive() && !adt.did.is_local() && !etc {
span_err!(tcx.sess, span, E0638,
"`..` required with {} marked as non-exhaustive",
kind_name);
// re-link the regions that EIfEO can erase.
self.demand_eqtype(span, adt_ty_hint, adt_ty);
- let (substs, adt_kind, kind_name) = match &adt_ty.sty{
+ let (substs, adt_kind, kind_name) = match &adt_ty.sty {
&ty::Adt(adt, substs) => {
(substs, adt.adt_kind(), adt.variant_descr())
}
base_expr: &'gcx Option<P<hir::Expr>>) -> Ty<'tcx>
{
// Find the relevant variant
- let (variant, struct_ty) =
- if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) {
- variant_ty
- } else {
- self.check_struct_fields_on_error(fields, base_expr);
- return self.tcx.types.err;
- };
+ let (variant, adt_ty) =
+ if let Some(variant_ty) = self.check_struct_path(qpath, expr.id) {
+ variant_ty
+ } else {
+ self.check_struct_fields_on_error(fields, base_expr);
+ return self.tcx.types.err;
+ };
let path_span = match *qpath {
hir::QPath::Resolved(_, ref path) => path.span,
};
// Prohibit struct expressions when non exhaustive flag is set.
- if let ty::Adt(adt, _) = struct_ty.sty {
- if !adt.did.is_local() && adt.is_non_exhaustive() {
- span_err!(self.tcx.sess, expr.span, E0639,
- "cannot create non-exhaustive {} using struct expression",
- adt.variant_descr());
- }
+ let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type");
+ if !adt.did.is_local() && variant.is_field_list_non_exhaustive() {
+ span_err!(self.tcx.sess, expr.span, E0639,
+ "cannot create non-exhaustive {} using struct expression",
+ adt.variant_descr());
}
- let error_happened = self.check_expr_struct_fields(struct_ty, expected, expr.id, path_span,
+ let error_happened = self.check_expr_struct_fields(adt_ty, expected, expr.id, path_span,
variant, fields, base_expr.is_none());
if let &Some(ref base_expr) = base_expr {
// If check_expr_struct_fields hit an error, do not attempt to populate
// the fields with the base_expr. This could cause us to hit errors later
// when certain fields are assumed to exist that in fact do not.
if !error_happened {
- self.check_expr_has_type_or_error(base_expr, struct_ty);
- match struct_ty.sty {
+ self.check_expr_has_type_or_error(base_expr, adt_ty);
+ match adt_ty.sty {
ty::Adt(adt, substs) if adt.is_struct() => {
let fru_field_types = adt.non_enum_variant().fields.iter().map(|f| {
self.normalize_associated_types_in(expr.span, &f.ty(self.tcx, substs))
}
}
}
- self.require_type_is_sized(struct_ty, expr.span, traits::StructInitializerSized);
- struct_ty
+ self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized);
+ adt_ty
}
}
}
-fn convert_struct_variant<'a, 'tcx>(
+fn convert_variant<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
did: DefId,
name: ast::Name,
discr: ty::VariantDiscr,
def: &hir::VariantData,
+ adt_kind: ty::AdtKind
) -> ty::VariantDef {
let mut seen_fields: FxHashMap<ast::Ident, Span> = FxHashMap();
let node_id = tcx.hir.as_local_node_id(did).unwrap();
}
})
.collect();
- ty::VariantDef {
+ ty::VariantDef::new(tcx,
did,
name,
discr,
fields,
- ctor_kind: CtorKind::from_hir(def),
- }
+ adt_kind,
+ CtorKind::from_hir(def))
}
fn adt_def<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx ty::AdtDef {
};
distance_from_explicit += 1;
- convert_struct_variant(tcx, did, v.node.name, discr, &v.node.data)
+ convert_variant(tcx, did, v.node.name, discr, &v.node.data, AdtKind::Enum)
})
.collect(),
)
};
(
AdtKind::Struct,
- vec![convert_struct_variant(
+ vec![convert_variant(
tcx,
ctor_id.unwrap_or(def_id),
item.name,
ty::VariantDiscr::Relative(0),
def,
+ AdtKind::Struct
)],
)
}
ItemKind::Union(ref def, _) => (
AdtKind::Union,
- vec![convert_struct_variant(
+ vec![convert_variant(
tcx,
def_id,
item.name,
ty::VariantDiscr::Relative(0),
def,
+ AdtKind::Union
)],
),
_ => bug!(),
&hir::WherePredicate::BoundPredicate(ref bound_pred) => {
let ty = icx.to_ty(&bound_pred.bounded_ty);
+ // Keep the type around in a WF predicate, in case of no bounds.
+ // That way, `where Ty:` is not a complete noop (see #53696).
+ if bound_pred.bounds.is_empty() {
+ if let ty::Param(_) = ty.sty {
+ // This is a `where T:`, which can be in the HIR from the
+ // transformation that moves `?Sized` to `T`'s declaration.
+ // We can skip the predicate because type parameters are
+ // trivially WF, but also we *should*, to avoid exposing
+ // users who never wrote `where Type:,` themselves, to
+ // compiler/tooling bugs from not handling WF predicates.
+ } else {
+ predicates.push(ty::Predicate::WellFormed(ty));
+ }
+ }
+
for bound in bound_pred.bounds.iter() {
match bound {
&hir::GenericBound::Trait(ref poly_trait_ref, _) => {
Predicate::RegionOutlives(ref pred) => pred.clean(cx),
Predicate::TypeOutlives(ref pred) => pred.clean(cx),
Predicate::Projection(ref pred) => pred.clean(cx),
- Predicate::WellFormed(_) => panic!("not user writable"),
+ Predicate::WellFormed(ty) => {
+ // This comes from `where Ty:` (i.e. no bounds) (see #53696).
+ WherePredicate::BoundPredicate {
+ ty: ty.clean(cx),
+ bounds: vec![],
+ }
+ }
Predicate::ObjectSafe(_) => panic!("not user writable"),
Predicate::ClosureKind(..) => panic!("not user writable"),
Predicate::ConstEvaluatable(..) => panic!("not user writable"),
/// On success, the total number of bytes copied is returned and it is equal to
/// the length of the `to` file as reported by `metadata`.
///
+/// If you’re wanting to copy the contents of one file to another and you’re
+/// working with [`File`]s, see the [`io::copy`] function.
+///
+/// [`io::copy`]: ../io/fn.copy.html
+/// [`File`]: ./struct.File.html
+///
/// # Platform-specific behavior
///
/// This function currently corresponds to the `open` function in Unix
/// On success, the total number of bytes that were copied from
/// `reader` to `writer` is returned.
///
+/// If you’re wanting to copy the contents of one file to another and you’re
+/// working with filesystem paths, see the [`fs::copy`] function.
+///
+/// [`fs::copy`]: ../fs/fn.copy.html
+///
/// # Errors
///
/// This function will return an error immediately if any call to `read` or
--- /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.
+
+// edition:2018
+
+#![feature(uniform_paths)]
+
+use std;
+
+mod foo {
+ pub use std as my_std;
+}
+
+mod bar {
+ pub use std::{self};
+}
+
+fn main() {
+ self::std::io::stdout();
+ foo::my_std::io::stdout();
+ bar::std::io::stdout();
+}
match enum_unit {
_ => "no error with only wildcard"
};
+
+
+ // issue #53549 - check that variant constructors can still be called normally.
+
+ match NonExhaustiveEnum::Unit {
+ NonExhaustiveEnum::Unit => {},
+ _ => {}
+ };
+
+ match NonExhaustiveEnum::Tuple(2) {
+ NonExhaustiveEnum::Tuple(2) => {},
+ _ => {}
+ };
+
+ match (NonExhaustiveEnum::Unit {}) {
+ NonExhaustiveEnum::Unit {} => {},
+ _ => {}
+ };
+
+ match (NonExhaustiveEnum::Tuple { 0: 2 }) {
+ NonExhaustiveEnum::Tuple { 0: 2 } => {},
+ _ => {}
+ };
+
+ match (NonExhaustiveEnum::Struct { field: 2 }) {
+ NonExhaustiveEnum::Struct { field: 2 } => {},
+ _ => {}
+ };
+
}
use std::borrow::Borrow;
#[rustc_dump_program_clauses] //~ ERROR program clause dump
-trait Foo<'a, 'b, S, T, U> where S: Debug, T: Borrow<U>, U: ?Sized, 'a: 'b, U: 'b {
+trait Foo<'a, 'b, S, T, U>
+where
+ S: Debug,
+ T: Borrow<U>,
+ U: ?Sized,
+ 'a: 'b,
+ U: 'b,
+ Vec<T>:, // NOTE(#53696) this checks an empty list of bounds.
+{
fn s(_: S) -> S;
fn t(_: T) -> T;
fn u(_: U) -> U;
= note: Implemented(Self: Foo<'a, 'b, S, T, U>) :- FromEnv(Self: Foo<'a, 'b, S, T, U>).
= note: RegionOutlives('a : 'b) :- FromEnv(Self: Foo<'a, 'b, S, T, U>).
= note: TypeOutlives(U : 'b) :- FromEnv(Self: Foo<'a, 'b, S, T, U>).
- = note: WellFormed(Self: Foo<'a, 'b, S, T, U>) :- Implemented(Self: Foo<'a, 'b, S, T, U>), WellFormed(S: std::marker::Sized), WellFormed(T: std::marker::Sized), WellFormed(S: std::fmt::Debug), WellFormed(T: std::borrow::Borrow<U>), RegionOutlives('a : 'b), TypeOutlives(U : 'b).
+ = note: WellFormed(Self: Foo<'a, 'b, S, T, U>) :- Implemented(Self: Foo<'a, 'b, S, T, U>), WellFormed(S: std::marker::Sized), WellFormed(T: std::marker::Sized), WellFormed(S: std::fmt::Debug), WellFormed(T: std::borrow::Borrow<U>), RegionOutlives('a : 'b), TypeOutlives(U : 'b), WellFormed(std::vec::Vec<T>).
+ = note: WellFormed(std::vec::Vec<T>) :- FromEnv(Self: Foo<'a, 'b, S, T, U>).
error: aborting due to previous error
--- /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.
+
+#![feature(const_let)]
+
+fn main() {
+ let _ = [(); {
+ //~^ WARNING Constant evaluating a complex constant, this might take some time
+ //~| ERROR could not evaluate repeat length
+ let mut x = &0;
+ let mut n = 0;
+ while n < 5 { //~ ERROR constant contains unimplemented expression type
+ n = (n + 1) % 5;
+ x = &0; // Materialize a new AllocId
+ }
+ 0
+ }];
+}
--- /dev/null
+error[E0019]: constant contains unimplemented expression type
+ --> $DIR/issue-52475.rs:19:9
+ |
+LL | / while n < 5 { //~ ERROR constant contains unimplemented expression type
+LL | | n = (n + 1) % 5;
+LL | | x = &0; // Materialize a new AllocId
+LL | | }
+ | |_________^
+
+warning: Constant evaluating a complex constant, this might take some time
+ --> $DIR/issue-52475.rs:14:18
+ |
+LL | let _ = [(); {
+ | __________________^
+LL | | //~^ WARNING Constant evaluating a complex constant, this might take some time
+LL | | //~| ERROR could not evaluate repeat length
+LL | | let mut x = &0;
+... |
+LL | | 0
+LL | | }];
+ | |_____^
+
+error[E0080]: could not evaluate repeat length
+ --> $DIR/issue-52475.rs:14:18
+ |
+LL | let _ = [(); {
+ | __________________^
+LL | | //~^ WARNING Constant evaluating a complex constant, this might take some time
+LL | | //~| ERROR could not evaluate repeat length
+LL | | let mut x = &0;
+... |
+LL | | n = (n + 1) % 5;
+ | | ----------- duplicate interpreter state observed here, const evaluation will never terminate
+... |
+LL | | 0
+LL | | }];
+ | |_____^
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0019, E0080.
+For more information about an error, try `rustc --explain E0019`.
LL | S[0];
| ^^^^
-error[E0507]: cannot move out of borrowed content
- --> $DIR/dst-index.rs:41:5
- |
-LL | S[0];
- | ^^^^ cannot move out of borrowed content
-
error[E0161]: cannot move a value of type dyn std::fmt::Debug: the size of dyn std::fmt::Debug cannot be statically determined
--> $DIR/dst-index.rs:44:5
|
LL | T[0];
| ^^^^
+error[E0507]: cannot move out of borrowed content
+ --> $DIR/dst-index.rs:41:5
+ |
+LL | S[0];
+ | ^^^^ cannot move out of borrowed content
+
error[E0507]: cannot move out of borrowed content
--> $DIR/dst-index.rs:44:5
|
LL | let _x: Box<str> = box *"hello world";
| ^^^^^^^^^^^^^^
-error[E0507]: cannot move out of borrowed content
- --> $DIR/dst-rvalue.rs:16:28
- |
-LL | let _x: Box<str> = box *"hello world";
- | ^^^^^^^^^^^^^^ cannot move out of borrowed content
-
error[E0161]: cannot move a value of type [isize]: the size of [isize] cannot be statically determined
--> $DIR/dst-rvalue.rs:21:32
|
LL | let _x: Box<[isize]> = box *array;
| ^^^^^^
+error[E0507]: cannot move out of borrowed content
+ --> $DIR/dst-rvalue.rs:16:28
+ |
+LL | let _x: Box<str> = box *"hello world";
+ | ^^^^^^^^^^^^^^ cannot move out of borrowed content
+
error[E0508]: cannot move out of type `[isize]`, a non-copy slice
--> $DIR/dst-rvalue.rs:21:32
|
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:9
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:5
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:9
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:5
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:9
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:5
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
// option. This file may not be copied, modified, or distributed
// except according to those terms.
+// ignore-compare-mode-nll
+
+// Check that E0161 is a hard error in all possible configurations that might
+// affect it.
+
+// revisions: ast nll zflags edition astul nllul zflagsul editionul
+//[zflags]compile-flags: -Z borrowck=migrate -Z two-phase-borrows
+//[edition]edition:2018
+//[zflagsul]compile-flags: -Z borrowck=migrate -Z two-phase-borrows
+//[editionul]edition:2018
+
+#![cfg_attr(nll, feature(nll))]
+#![cfg_attr(nllul, feature(nll))]
+#![cfg_attr(astul, feature(unsized_locals))]
+#![cfg_attr(zflagsul, feature(unsized_locals))]
+#![cfg_attr(nllul, feature(unsized_locals))]
+#![cfg_attr(editionul, feature(unsized_locals))]
+
#![feature(box_syntax)]
-fn main() {
- let _x: Box<str> = box *"hello"; //~ ERROR E0161
- //~^ ERROR E0507
+fn foo(x: Box<[i32]>) {
+ box *x; //~ ERROR E0161
}
+
+fn main() {}
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:9
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
--- /dev/null
+error[E0161]: cannot move a value of type [i32]: the size of [i32] cannot be statically determined
+ --> $DIR/E0161.rs:32:5
+ |
+LL | box *x; //~ ERROR E0161
+ | ^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0161`.
+++ /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.
-
-// edition:2018
-
-#![feature(uniform_paths)]
-
-use std;
-
-fn main() {}
+++ /dev/null
-error: `std` import is redundant
- --> $DIR/redundant.rs:15:5
- |
-LL | use std;
- | ^^^
- | |
- | refers to external crate `::std`
- | defines `self::std`, shadowing itself
- |
- = help: remove or write `::std` explicitly instead
- = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
-
-error: aborting due to previous error
-
// Test that we check where-clauses on fn items.
-#![feature(associated_type_defaults)]
#![feature(rustc_attrs)]
#![allow(dead_code)]
{
}
+fn bar() where Vec<dyn Copy>:, {}
+//~^ ERROR E0277
+//~| ERROR E0038
+
#[rustc_error]
fn main() { }
error[E0277]: the trait bound `U: std::marker::Copy` is not satisfied
- --> $DIR/wf-fn-where-clause.rs:19:1
+ --> $DIR/wf-fn-where-clause.rs:18:1
|
LL | / fn foo<T,U>() where T: ExtraCopy<U> //~ ERROR E0277
LL | | {
|
= help: consider adding a `where U: std::marker::Copy` bound
note: required by `ExtraCopy`
- --> $DIR/wf-fn-where-clause.rs:17:1
+ --> $DIR/wf-fn-where-clause.rs:16:1
|
LL | trait ExtraCopy<T:Copy> { }
| ^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error[E0277]: the size for values of type `(dyn std::marker::Copy + 'static)` cannot be known at compilation time
+ --> $DIR/wf-fn-where-clause.rs:22:1
+ |
+LL | fn bar() where Vec<dyn Copy>:, {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = help: the trait `std::marker::Sized` is not implemented for `(dyn std::marker::Copy + 'static)`
+ = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
+ = note: required by `std::vec::Vec`
+
+error[E0038]: the trait `std::marker::Copy` cannot be made into an object
+ --> $DIR/wf-fn-where-clause.rs:22:1
+ |
+LL | fn bar() where Vec<dyn Copy>:, {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` cannot be made into an object
+ |
+ = note: the trait cannot require that `Self : Sized`
+
+error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0277`.
+Some errors occurred: E0038, E0277.
+For more information about an error, try `rustc --explain E0038`.
-Subproject commit 2a284a70e26997273c296afe06586ffdf3a142fd
+Subproject commit d0fc1788123de9844c8088b977cd142021cea1f2
-Subproject commit 3dbe998969d457c5cef245f61b48bdaed0f5c059
+Subproject commit 7728fa22bebea288abfea3b70cf795c60b93df3a