Currently building Rust will also build the following external projects:
* [clippy](https://github.com/rust-lang-nursery/rust-clippy)
+* [miri](https://github.com/solson/miri)
If your changes break one of these projects, you need to fix them by opening
a pull request against the broken project. When you have opened a pull request,
-you can point the submodule at your pull request by calling
-
-```
-git fetch origin pull/$id_of_your_pr/head:my_pr
-git checkout my_pr
-```
-
-within the submodule's directory. Don't forget to also add your changes with
-
-```
-git add path/to/submodule
-```
-
-outside the submodule.
+you can disable the tool via `src/tools/toolstate.toml`.
It can also be more convenient during development to set `submodules = false`
in the `config.toml` to prevent `x.py` from resetting to the original branch.
try:
with open(args.config or 'config.toml') as config:
build.config_toml = config.read()
- except OSError:
+ except (OSError, IOError):
pass
if '\nverbose = 2' in build.config_toml:
Kind::Test => describe!(check::Tidy, check::Bootstrap, check::DefaultCompiletest,
check::HostCompiletest, check::Crate, check::CrateLibrustc, check::Rustdoc,
check::Linkcheck, check::Cargotest, check::Cargo, check::Rls, check::Docs,
- check::ErrorIndex, check::Distcheck, check::Rustfmt, check::Miri),
+ check::ErrorIndex, check::Distcheck, check::Rustfmt, check::Miri, check::Clippy),
Kind::Bench => describe!(check::Crate, check::CrateLibrustc),
Kind::Doc => describe!(doc::UnstableBook, doc::UnstableBookGen, doc::TheBook,
doc::Standalone, doc::Std, doc::Test, doc::Rustc, doc::ErrorIndex, doc::Nomicon,
}
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Clippy {
+ host: Interned<String>,
+}
+
+impl Step for Clippy {
+ type Output = ();
+ const ONLY_HOSTS: bool = true;
+ const DEFAULT: bool = false;
+
+ fn should_run(run: ShouldRun) -> ShouldRun {
+ run.path("src/tools/clippy")
+ }
+
+ fn make_run(run: RunConfig) {
+ run.builder.ensure(Clippy {
+ host: run.target,
+ });
+ }
+
+ /// Runs `cargo test` for clippy.
+ fn run(self, builder: &Builder) {
+ let build = builder.build;
+ let host = self.host;
+ let compiler = builder.compiler(1, host);
+
+ let _clippy = builder.ensure(tool::Clippy { compiler, target: self.host });
+ let mut cargo = builder.cargo(compiler, Mode::Tool, host, "test");
+ cargo.arg("--manifest-path").arg(build.src.join("src/tools/clippy/Cargo.toml"));
+
+ // Don't build tests dynamically, just a pain to work with
+ cargo.env("RUSTC_NO_PREFER_DYNAMIC", "1");
+ // clippy tests need to know about the stage sysroot
+ cargo.env("SYSROOT", builder.sysroot(compiler));
+
+ builder.add_rustc_lib_path(compiler, &mut cargo);
+
+ try_run_expecting(
+ build,
+ &mut cargo,
+ builder.build.config.toolstate.clippy.passes(ToolState::Testing),
+ );
+ }
+}
fn path_for_cargo(builder: &Builder, compiler: Compiler) -> OsString {
// Configure PATH to find the right rustc. NB. we have to use PATH
tool: "clippy",
mode: Mode::Librustc,
path: "src/tools/clippy",
- expectation: BuildExpectation::None,
+ expectation: builder.build.config.toolstate.clippy.passes(ToolState::Compiling),
})
}
}
/// This is created from `toolstate.toml`.
pub struct ToolStates {
pub miri: ToolState,
+ pub clippy: ToolState,
}
#![feature(generic_param_attrs)]
#![feature(i128_type)]
#![feature(inclusive_range)]
+#![feature(iter_rfold)]
#![feature(lang_items)]
#![feature(needs_allocator)]
#![feature(nonzero)]
self.del += 1;
return Some(ptr::read(&v[i]));
} else if self.del > 0 {
- v.swap(i - self.del, i);
+ let del = self.del;
+ let src: *const T = &v[i];
+ let dst: *mut T = &mut v[i - del];
+ // This is safe because self.vec has length 0
+ // thus its elements will not have Drop::drop
+ // called on them in the event of a panic.
+ ptr::copy_nonoverlapping(src, dst, 1);
}
}
None
self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len());
unsafe { Some(self.ring.get_unchecked(self.head)) }
}
+
+ fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
+ where F: FnMut(Acc, Self::Item) -> Acc
+ {
+ let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
+ accum = back.iter().rfold(accum, &mut f);
+ front.iter().rfold(accum, &mut f)
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
Some(&mut *(elem as *mut _))
}
}
+
+ fn rfold<Acc, F>(self, mut accum: Acc, mut f: F) -> Acc
+ where F: FnMut(Acc, Self::Item) -> Acc
+ {
+ let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail);
+ accum = back.iter_mut().rfold(accum, &mut f);
+ front.iter_mut().rfold(accum, &mut f)
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
(left, right)
}
- /// An iterator adaptor that applies a function, producing a single, final value.
+ /// An iterator method that applies a function, producing a single, final value.
///
/// `fold()` takes two arguments: an initial value, and a closure with two
/// arguments: an 'accumulator', and an element. The closure returns the value that
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+ fn fold<Acc, F>(self, init: Acc, f: F) -> Acc
+ where F: FnMut(Acc, Self::Item) -> Acc,
+ {
+ self.iter.rfold(init, f)
+ }
+
#[inline]
fn find<P>(&mut self, predicate: P) -> Option<Self::Item>
where P: FnMut(&Self::Item) -> bool
#[inline]
fn next_back(&mut self) -> Option<<I as Iterator>::Item> { self.iter.next() }
+ fn rfold<Acc, F>(self, init: Acc, f: F) -> Acc
+ where F: FnMut(Acc, Self::Item) -> Acc,
+ {
+ self.iter.fold(init, f)
+ }
+
fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
where P: FnMut(&Self::Item) -> bool
{
fn next_back(&mut self) -> Option<T> {
self.it.next_back().cloned()
}
+
+ fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
+ where F: FnMut(Acc, Self::Item) -> Acc,
+ {
+ self.it.rfold(init, move |acc, elt| f(acc, elt.clone()))
+ }
}
#[stable(feature = "iter_cloned", since = "1.1.0")]
ChainState::Back => self.b.next_back(),
}
}
+
+ fn rfold<Acc, F>(self, init: Acc, mut f: F) -> Acc
+ where F: FnMut(Acc, Self::Item) -> Acc,
+ {
+ let mut accum = init;
+ match self.state {
+ ChainState::Both | ChainState::Back => {
+ accum = self.b.rfold(accum, &mut f);
+ }
+ _ => { }
+ }
+ match self.state {
+ ChainState::Both | ChainState::Front => {
+ accum = self.a.rfold(accum, &mut f);
+ }
+ _ => { }
+ }
+ accum
+ }
+
}
// Note: *both* must be fused to handle double-ended iterators.
fn next_back(&mut self) -> Option<B> {
self.iter.next_back().map(&mut self.f)
}
+
+ fn rfold<Acc, G>(self, init: Acc, mut g: G) -> Acc
+ where G: FnMut(Acc, Self::Item) -> Acc,
+ {
+ let mut f = self.f;
+ self.iter.rfold(init, move |acc, elt| g(acc, f(elt)))
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
#[stable(feature = "rust1", since = "1.0.0")]
fn next_back(&mut self) -> Option<Self::Item>;
+ /// An iterator method that reduces the iterator's elements to a single,
+ /// final value, starting from the back.
+ ///
+ /// This is the reverse version of [`fold()`]: it takes elements starting from
+ /// the back of the iterator.
+ ///
+ /// `rfold()` takes two arguments: an initial value, and a closure with two
+ /// arguments: an 'accumulator', and an element. The closure returns the value that
+ /// the accumulator should have for the next iteration.
+ ///
+ /// The initial value is the value the accumulator will have on the first
+ /// call.
+ ///
+ /// After applying this closure to every element of the iterator, `rfold()`
+ /// returns the accumulator.
+ ///
+ /// This operation is sometimes called 'reduce' or 'inject'.
+ ///
+ /// Folding is useful whenever you have a collection of something, and want
+ /// to produce a single value from it.
+ ///
+ /// [`fold()`]: trait.Iterator.html#method.fold
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(iter_rfold)]
+ /// let a = [1, 2, 3];
+ ///
+ /// // the sum of all of the elements of a
+ /// let sum = a.iter()
+ /// .rfold(0, |acc, &x| acc + x);
+ ///
+ /// assert_eq!(sum, 6);
+ /// ```
+ ///
+ /// This example builds a string, starting with an initial value
+ /// and continuing with each element from the back until the front:
+ ///
+ /// ```
+ /// #![feature(iter_rfold)]
+ /// let numbers = [1, 2, 3, 4, 5];
+ ///
+ /// let zero = "0".to_string();
+ ///
+ /// let result = numbers.iter().rfold(zero, |acc, &x| {
+ /// format!("({} + {})", x, acc)
+ /// });
+ ///
+ /// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))");
+ /// ```
+ #[inline]
+ #[unstable(feature = "iter_rfold", issue = "44705")]
+ fn rfold<B, F>(mut self, mut accum: B, mut f: F) -> B where
+ Self: Sized, F: FnMut(B, Self::Item) -> B,
+ {
+ while let Some(x) = self.next_back() {
+ accum = f(accum, x);
+ }
+ accum
+ }
+
/// Searches for an element of an iterator from the right that satisfies a predicate.
///
/// `rfind()` takes a closure that returns `true` or `false`. It applies
/// Returns the size of a type in bytes.
///
-/// More specifically, this is the offset in bytes between successive
-/// items of the same type, including alignment padding.
+/// More specifically, this is the offset in bytes between successive elements
+/// in an array with that item type including alignment padding. Thus, for any
+/// type `T` and length `n`, `[T; n]` has a size of `n * size_of::<T>()`.
+///
+/// In general, the size of a type is not stable across compilations, but
+/// specific types such as primitives are.
+///
+/// The following table gives the size for primitives.
+///
+/// Type | size_of::\<Type>()
+/// ---- | ---------------
+/// () | 0
+/// u8 | 1
+/// u16 | 2
+/// u32 | 4
+/// u64 | 8
+/// i8 | 1
+/// i16 | 2
+/// i32 | 4
+/// i64 | 8
+/// f32 | 4
+/// f64 | 8
+/// char | 4
+///
+/// Furthermore, `usize` and `isize` have the same size.
+///
+/// The types `*const T`, `&T`, `Box<T>`, `Option<&T>`, and `Option<Box<T>>` all have
+/// the same size. If `T` is Sized, all of those types have the same size as `usize`.
+///
+/// The mutability of a pointer does not change its size. As such, `&T` and `&mut T`
+/// have the same size. Likewise for `*const T` and `*mut T`.
///
/// # Examples
///
/// ```
/// use std::mem;
///
+/// // Some primitives
/// assert_eq!(4, mem::size_of::<i32>());
+/// assert_eq!(8, mem::size_of::<f64>());
+/// assert_eq!(0, mem::size_of::<()>());
+///
+/// // Some arrays
+/// assert_eq!(8, mem::size_of::<[i32; 2]>());
+/// assert_eq!(12, mem::size_of::<[i32; 3]>());
+/// assert_eq!(0, mem::size_of::<[i32; 0]>());
+///
+///
+/// // Pointer size equality
+/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::<*const i32>());
+/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::<Box<i32>>());
+/// assert_eq!(mem::size_of::<&i32>(), mem::size_of::<Option<&i32>>());
+/// assert_eq!(mem::size_of::<Box<i32>>(), mem::size_of::<Option<Box<i32>>>());
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
*/
/// Bytewise slice equality
-/// NOTE: This function is (ab)used in rustc::middle::trans::_match
-/// to compare &[u8] byte slices that are not necessarily valid UTF-8.
-#[lang = "str_eq"]
#[inline]
fn eq_slice(a: &str, b: &str) -> bool {
a.as_bytes() == b.as_bytes()
($x:tt) => ({})
}
-macro_rules! anon_attr_to_bool {
- (anon) => (true)
+macro_rules! is_anon_attr {
+ (anon) => (true);
+ ($attr:ident) => (false);
+}
+
+macro_rules! is_input_attr {
+ (input) => (true);
+ ($attr:ident) => (false);
+}
+
+macro_rules! contains_anon_attr {
+ ($($attr:ident),*) => ({$(is_anon_attr!($attr) | )* false});
+}
+
+macro_rules! contains_input_attr {
+ ($($attr:ident),*) => ({$(is_input_attr!($attr) | )* false});
}
macro_rules! define_dep_nodes {
(<$tcx:tt>
$(
- [$($anon:ident)*]
+ [$($attr:ident),* ]
$variant:ident $(( $($tuple_arg:tt),* ))*
$({ $($struct_arg_name:ident : $struct_arg_ty:ty),* })*
,)*
match *self {
$(
DepKind :: $variant => {
- $(return !anon_attr_to_bool!($anon);)*
+ if contains_anon_attr!($($attr),*) {
+ return false;
+ }
// tuple args
$({
}
}
- #[allow(unreachable_code)]
#[inline]
- pub fn is_anon<$tcx>(&self) -> bool {
+ pub fn is_anon(&self) -> bool {
match *self {
$(
- DepKind :: $variant => {
- $(return anon_attr_to_bool!($anon);)*
- false
- }
+ DepKind :: $variant => { contains_anon_attr!($($attr),*) }
+ )*
+ }
+ }
+
+ #[inline]
+ pub fn is_input(&self) -> bool {
+ match *self {
+ $(
+ DepKind :: $variant => { contains_input_attr!($($attr),*) }
)*
}
}
}
}
+impl DepKind {
+ #[inline]
+ pub fn fingerprint_needed_for_crate_hash(self) -> bool {
+ match self {
+ DepKind::HirBody |
+ DepKind::Krate => true,
+ _ => false,
+ }
+ }
+}
+
define_dep_nodes!( <'tcx>
// Represents the `Krate` as a whole (the `hir::Krate` value) (as
// distinct from the krate module). This is basically a hash of
// suitable wrapper, you can use `tcx.dep_graph.ignore()` to gain
// access to the krate, but you must remember to add suitable
// edges yourself for the individual items that you read.
- [] Krate,
-
- // Represents the HIR node with the given node-id
- [] Hir(DefId),
+ [input] Krate,
// Represents the body of a function or method. The def-id is that of the
// function/method.
- [] HirBody(DefId),
+ [input] HirBody(DefId),
+
+ // Represents the HIR node with the given node-id
+ [input] Hir(DefId),
- // Represents the metadata for a given HIR node, typically found
- // in an extern crate.
- [] MetaData(DefId),
+ // Represents metadata from an extern crate.
+ [input] MetaData(DefId),
// Represents some artifact that we save to disk. Note that these
// do not have a def-id as part of their identifier.
[] ExternCrate(DefId),
[] LintLevels,
[] Specializes { impl1: DefId, impl2: DefId },
- [] InScopeTraits(DefIndex),
+ [input] InScopeTraits(DefIndex),
[] ModuleExports(DefId),
[] IsSanitizerRuntime(CrateNum),
[] IsProfilerRuntime(CrateNum),
[] MissingExternCrateItem(CrateNum),
[] UsedCrateSource(CrateNum),
[] PostorderCnums,
+ [] HasCloneClosures(CrateNum),
+ [] HasCopyClosures(CrateNum),
[] Freevars(DefId),
[] MaybeUnusedTraitImport(DefId),
reads
} = popped_node {
debug_assert_eq!(node, key);
+ debug_assert!(!node.kind.is_input() || reads.is_empty());
let target_id = self.get_or_create_node(node);
#[derive(Clone)]
pub struct DepGraph {
- data: Option<Rc<DepGraphData>>
+ data: Option<Rc<DepGraphData>>,
+
+ // At the moment we are using DepNode as key here. In the future it might
+ // be possible to use an IndexVec<DepNodeIndex, _> here. At the moment there
+ // are a few problems with that:
+ // - Some fingerprints are needed even if incr. comp. is disabled -- yet
+ // we need to have a dep-graph to generate DepNodeIndices.
+ // - The architecture is still in flux and it's not clear what how to best
+ // implement things.
+ fingerprints: Rc<RefCell<FxHashMap<DepNode, Fingerprint>>>
}
struct DepGraphData {
}))
} else {
None
- }
+ },
+ fingerprints: Rc::new(RefCell::new(FxHashMap())),
}
}
let mut stable_hasher = StableHasher::new();
result.hash_stable(&mut hcx, &mut stable_hasher);
- let _: Fingerprint = stable_hasher.finish();
+
+ assert!(self.fingerprints
+ .borrow_mut()
+ .insert(key, stable_hasher.finish())
+ .is_none());
(result, dep_node_index)
} else {
- (task(cx, arg), DepNodeIndex::INVALID)
+ if key.kind.fingerprint_needed_for_crate_hash() {
+ let mut hcx = cx.create_stable_hashing_context();
+ let result = task(cx, arg);
+ let mut stable_hasher = StableHasher::new();
+ result.hash_stable(&mut hcx, &mut stable_hasher);
+ assert!(self.fingerprints
+ .borrow_mut()
+ .insert(key, stable_hasher.finish())
+ .is_none());
+ (result, DepNodeIndex::INVALID)
+ } else {
+ (task(cx, arg), DepNodeIndex::INVALID)
+ }
}
}
}
}
+ pub fn fingerprint_of(&self, dep_node: &DepNode) -> Option<Fingerprint> {
+ self.fingerprints.borrow().get(dep_node).cloned()
+ }
+
/// Indicates that a previous work product exists for `v`. This is
/// invoked during initial start-up based on what nodes are clean
/// (and what files exist in the incr. directory).
E0566, // conflicting representation hints
E0623, // lifetime mismatch where both parameters are anonymous regions
E0628, // generators cannot have explicit arguments
+ E0637, // "'_" is not a valid lifetime bound
}
//! for more details.
//!
//! Note: it is an important invariant that the default visitor walks
-//! the body of a function in "execution order" (more concretely,
-//! reverse post-order with respect to the CFG implied by the AST),
-//! meaning that if AST node A may execute before AST node B, then A
-//! is visited first. The borrow checker in particular relies on this
-//! property.
+//! the body of a function in "execution order" - more concretely, if
+//! we consider the reverse post-order (RPO) of the CFG implied by the HIR,
+//! then a pre-order traversal of the HIR is consistent with the CFG RPO
+//! on the *initial CFG point* of each HIR node, while a post-order traversal
+//! of the HIR is consistent with the CFG RPO on each *final CFG point* of
+//! each CFG node.
+//!
+//! One thing that follows is that if HIR node A always starts/ends executing
+//! before HIR node B, then A appears in traversal pre/postorder before B,
+//! respectively. (This follows from RPO respecting CFG domination).
+//!
+//! This order consistency is required in a few places in rustc, for
+//! example generator inference, and possibly also HIR borrowck.
use syntax::abi::Abi;
use syntax::ast::{NodeId, CRATE_NODE_ID, Name, Attribute};
}
pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local) {
+ // Intentionally visiting the expr first - the initialization expr
+ // dominates the local's definition.
+ walk_list!(visitor, visit_expr, &local.init);
+
visitor.visit_id(local.id);
visitor.visit_pat(&local.pat);
walk_list!(visitor, visit_ty, &local.ty);
- walk_list!(visitor, visit_expr, &local.init);
}
pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) {
visitor.visit_id(lifetime.id);
- visitor.visit_name(lifetime.span, lifetime.name);
+ match lifetime.name {
+ LifetimeName::Name(name) => {
+ visitor.visit_name(lifetime.span, name);
+ }
+ LifetimeName::Static | LifetimeName::Implicit | LifetimeName::Underscore => {}
+ }
}
pub fn walk_lifetime_def<'v, V: Visitor<'v>>(visitor: &mut V, lifetime_def: &'v LifetimeDef) {
fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
hir::Lifetime {
id: self.lower_node_id(l.id).node_id,
- name: self.lower_ident(l.ident),
+ name: match self.lower_ident(l.ident) {
+ x if x == "'_" => hir::LifetimeName::Underscore,
+ x if x == "'static" => hir::LifetimeName::Static,
+ name => hir::LifetimeName::Name(name),
+ },
span: l.span,
}
}
hir::Lifetime {
id: self.next_id().node_id,
span,
- name: keywords::Invalid.name()
+ name: hir::LifetimeName::Implicit,
}
}
}
use syntax::ast::{NodeId, CRATE_NODE_ID};
use syntax_pos::Span;
+use ich::StableHashingContext;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult};
+
/// A Visitor that walks over the HIR and collects Nodes into a HIR map
pub(super) struct NodeCollector<'a, 'hir> {
/// The crate
/// The parent of this node
parent_node: NodeId,
+ // These fields keep track of the currently relevant DepNodes during
+ // the visitor's traversal.
current_dep_node_owner: DefIndex,
- current_dep_node_index: DepNodeIndex,
+ current_signature_dep_index: DepNodeIndex,
+ current_full_dep_index: DepNodeIndex,
+ currently_in_body: bool,
dep_graph: &'a DepGraph,
definitions: &'a definitions::Definitions,
+
+ hcx: StableHashingContext<'a>,
+
+ // We are collecting DepNode::HirBody hashes here so we can compute the
+ // crate hash from then later on.
+ hir_body_nodes: Vec<DefPathHash>,
}
impl<'a, 'hir> NodeCollector<'a, 'hir> {
pub(super) fn root(krate: &'hir Crate,
- dep_graph: &'a DepGraph,
- definitions: &'a definitions::Definitions)
+ dep_graph: &'a DepGraph,
+ definitions: &'a definitions::Definitions,
+ hcx: StableHashingContext<'a>)
-> NodeCollector<'a, 'hir> {
let root_mod_def_path_hash = definitions.def_path_hash(CRATE_DEF_INDEX);
- let root_mod_dep_node = root_mod_def_path_hash.to_dep_node(DepKind::Hir);
- let root_mod_dep_node_index = dep_graph.alloc_input_node(root_mod_dep_node);
+
+ // Allocate DepNodes for the root module
+ let (root_mod_sig_dep_index, root_mod_full_dep_index);
+ {
+ let Crate {
+ ref module,
+ // Crate attributes are not copied over to the root `Mod`, so hash
+ // them explicitly here.
+ ref attrs,
+ span,
+
+ // These fields are handled separately:
+ exported_macros: _,
+ items: _,
+ trait_items: _,
+ impl_items: _,
+ bodies: _,
+ trait_impls: _,
+ trait_default_impl: _,
+ body_ids: _,
+ } = *krate;
+
+ root_mod_sig_dep_index = dep_graph.with_task(
+ root_mod_def_path_hash.to_dep_node(DepKind::Hir),
+ &hcx,
+ HirItemLike { item_like: (module, attrs, span), hash_bodies: false },
+ identity_fn
+ ).1;
+ root_mod_full_dep_index = dep_graph.with_task(
+ root_mod_def_path_hash.to_dep_node(DepKind::HirBody),
+ &hcx,
+ HirItemLike { item_like: (module, attrs, span), hash_bodies: true },
+ identity_fn
+ ).1;
+ }
+
+ let hir_body_nodes = vec![root_mod_def_path_hash];
let mut collector = NodeCollector {
krate,
map: vec![],
parent_node: CRATE_NODE_ID,
- current_dep_node_index: root_mod_dep_node_index,
+ current_signature_dep_index: root_mod_sig_dep_index,
+ current_full_dep_index: root_mod_full_dep_index,
current_dep_node_owner: CRATE_DEF_INDEX,
+ currently_in_body: false,
dep_graph,
definitions,
+ hcx,
+ hir_body_nodes,
};
- collector.insert_entry(CRATE_NODE_ID, RootCrate(root_mod_dep_node_index));
+ collector.insert_entry(CRATE_NODE_ID, RootCrate(root_mod_sig_dep_index));
collector
}
- pub(super) fn into_map(self) -> Vec<MapEntry<'hir>> {
+ pub(super) fn finalize_and_compute_crate_hash(self,
+ crate_disambiguator: &str)
+ -> Vec<MapEntry<'hir>> {
+ let mut node_hashes: Vec<_> = self
+ .hir_body_nodes
+ .iter()
+ .map(|&def_path_hash| {
+ let dep_node = def_path_hash.to_dep_node(DepKind::HirBody);
+ (def_path_hash, self.dep_graph.fingerprint_of(&dep_node))
+ })
+ .collect();
+
+ node_hashes.sort_unstable_by(|&(ref d1, _), &(ref d2, _)| d1.cmp(d2));
+
+ self.dep_graph.with_task(DepNode::new_no_params(DepKind::Krate),
+ &self.hcx,
+ (node_hashes, crate_disambiguator),
+ identity_fn);
self.map
}
fn insert(&mut self, id: NodeId, node: Node<'hir>) {
let parent = self.parent_node;
- let dep_node_index = self.current_dep_node_index;
+ let dep_node_index = if self.currently_in_body {
+ self.current_full_dep_index
+ } else {
+ self.current_signature_dep_index
+ };
let entry = match node {
NodeItem(n) => EntryItem(parent, dep_node_index, n),
NodeTyParam(n) => EntryTyParam(parent, dep_node_index, n),
NodeVisibility(n) => EntryVisibility(parent, dep_node_index, n),
NodeLocal(n) => EntryLocal(parent, dep_node_index, n),
+ NodeMacroDef(n) => EntryMacroDef(dep_node_index, n),
};
// Make sure that the DepNode of some node coincides with the HirId
self.parent_node = parent_node;
}
- fn with_dep_node_owner<F: FnOnce(&mut Self)>(&mut self,
+ fn with_dep_node_owner<T: HashStable<StableHashingContext<'a>>,
+ F: FnOnce(&mut Self)>(&mut self,
dep_node_owner: DefIndex,
+ item_like: &T,
f: F) {
let prev_owner = self.current_dep_node_owner;
- let prev_index = self.current_dep_node_index;
-
- // When we enter a new owner (item, impl item, or trait item), we always
- // start out again with DepKind::Hir.
- let new_dep_node = self.definitions
- .def_path_hash(dep_node_owner)
- .to_dep_node(DepKind::Hir);
- self.current_dep_node_index = self.dep_graph.alloc_input_node(new_dep_node);
+ let prev_signature_dep_index = self.current_signature_dep_index;
+ let prev_full_dep_index = self.current_signature_dep_index;
+ let prev_in_body = self.currently_in_body;
+
+ let def_path_hash = self.definitions.def_path_hash(dep_node_owner);
+
+ self.current_signature_dep_index = self.dep_graph.with_task(
+ def_path_hash.to_dep_node(DepKind::Hir),
+ &self.hcx,
+ HirItemLike { item_like, hash_bodies: false },
+ identity_fn
+ ).1;
+
+ self.current_full_dep_index = self.dep_graph.with_task(
+ def_path_hash.to_dep_node(DepKind::HirBody),
+ &self.hcx,
+ HirItemLike { item_like, hash_bodies: true },
+ identity_fn
+ ).1;
+
+ self.hir_body_nodes.push(def_path_hash);
+
self.current_dep_node_owner = dep_node_owner;
+ self.currently_in_body = false;
f(self);
- self.current_dep_node_index = prev_index;
+ self.currently_in_body = prev_in_body;
self.current_dep_node_owner = prev_owner;
+ self.current_full_dep_index = prev_full_dep_index;
+ self.current_signature_dep_index = prev_signature_dep_index;
}
}
}
fn visit_nested_body(&mut self, id: BodyId) {
- // When we enter a body, we switch to DepKind::HirBody.
- // Note that current_dep_node_index might already be DepKind::HirBody,
- // e.g. when entering the body of a closure that is already part of a
- // surrounding body. That's expected and not a problem.
- let prev_index = self.current_dep_node_index;
- let new_dep_node = self.definitions
- .def_path_hash(self.current_dep_node_owner)
- .to_dep_node(DepKind::HirBody);
- self.current_dep_node_index = self.dep_graph.alloc_input_node(new_dep_node);
+ let prev_in_body = self.currently_in_body;
+ self.currently_in_body = true;
self.visit_body(self.krate.body(id));
- self.current_dep_node_index = prev_index;
+ self.currently_in_body = prev_in_body;
}
fn visit_item(&mut self, i: &'hir Item) {
debug!("visit_item: {:?}", i);
debug_assert_eq!(i.hir_id.owner,
self.definitions.opt_def_index(i.id).unwrap());
- self.with_dep_node_owner(i.hir_id.owner, |this| {
+ self.with_dep_node_owner(i.hir_id.owner, i, |this| {
this.insert(i.id, NodeItem(i));
this.with_parent(i.id, |this| {
match i.node {
fn visit_trait_item(&mut self, ti: &'hir TraitItem) {
debug_assert_eq!(ti.hir_id.owner,
self.definitions.opt_def_index(ti.id).unwrap());
- self.with_dep_node_owner(ti.hir_id.owner, |this| {
+ self.with_dep_node_owner(ti.hir_id.owner, ti, |this| {
this.insert(ti.id, NodeTraitItem(ti));
this.with_parent(ti.id, |this| {
fn visit_impl_item(&mut self, ii: &'hir ImplItem) {
debug_assert_eq!(ii.hir_id.owner,
self.definitions.opt_def_index(ii.id).unwrap());
- self.with_dep_node_owner(ii.hir_id.owner, |this| {
+ self.with_dep_node_owner(ii.hir_id.owner, ii, |this| {
this.insert(ii.id, NodeImplItem(ii));
this.with_parent(ii.id, |this| {
}
fn visit_macro_def(&mut self, macro_def: &'hir MacroDef) {
- self.insert_entry(macro_def.id, NotPresent);
+ let def_index = self.definitions.opt_def_index(macro_def.id).unwrap();
+
+ self.with_dep_node_owner(def_index, macro_def, |this| {
+ this.insert(macro_def.id, NodeMacroDef(macro_def));
+ });
}
fn visit_variant(&mut self, v: &'hir Variant, g: &'hir Generics, item_id: NodeId) {
self.visit_nested_impl_item(id);
}
}
+
+// We use this with DepGraph::with_task(). Since we are handling only input
+// values here, the "task" computing them just passes them through.
+fn identity_fn<T>(_: &StableHashingContext, item_like: T) -> T {
+ item_like
+}
+
+// This is a wrapper structure that allows determining if span values within
+// the wrapped item should be hashed or not.
+struct HirItemLike<T> {
+ item_like: T,
+ hash_bodies: bool,
+}
+
+impl<'hir, T> HashStable<StableHashingContext<'hir>> for HirItemLike<T>
+ where T: HashStable<StableHashingContext<'hir>>
+{
+ fn hash_stable<W: StableHasherResult>(&self,
+ hcx: &mut StableHashingContext<'hir>,
+ hasher: &mut StableHasher<W>) {
+ hcx.while_hashing_hir_bodies(self.hash_bodies, |hcx| {
+ self.item_like.hash_stable(hcx, hasher);
+ });
+ }
+}
NodePat(&'hir Pat),
NodeBlock(&'hir Block),
NodeLocal(&'hir Local),
+ NodeMacroDef(&'hir MacroDef),
/// NodeStructCtor represents a tuple struct.
NodeStructCtor(&'hir VariantData),
EntryVisibility(NodeId, DepNodeIndex, &'hir Visibility),
EntryLocal(NodeId, DepNodeIndex, &'hir Local),
+ EntryMacroDef(DepNodeIndex, &'hir MacroDef),
+
/// Roots for node trees. The DepNodeIndex is the dependency node of the
/// crate's root module.
RootCrate(DepNodeIndex),
EntryLocal(id, _, _) => id,
NotPresent |
+ EntryMacroDef(..) |
RootCrate(_) => return None,
})
}
EntryTyParam(_, _, n) => NodeTyParam(n),
EntryVisibility(_, _, n) => NodeVisibility(n),
EntryLocal(_, _, n) => NodeLocal(n),
+ EntryMacroDef(_, n) => NodeMacroDef(n),
NotPresent |
RootCrate(_) => return None
EntryVisibility(_, dep_node_index, _) |
EntryExpr(_, dep_node_index, _) |
EntryLocal(_, dep_node_index, _) |
+ EntryMacroDef(dep_node_index, _) |
RootCrate(dep_node_index) => {
self.dep_graph.read_index(dep_node_index);
}
NotPresent => {
- // Some nodes, notably macro definitions, are not
- // present in the map for whatever reason, but
- // they *do* have def-ids. So if we encounter an
- // empty hole, check for that case.
- if let Some(def_index) = self.definitions.opt_def_index(id) {
- let def_path_hash = self.definitions.def_path_hash(def_index);
- self.dep_graph.read(def_path_hash.to_dep_node(DepKind::Hir));
- } else {
- bug!("called HirMap::read() with invalid NodeId")
- }
+ bug!("called HirMap::read() with invalid NodeId")
}
}
}
NodeTraitItem(ti) => ti.name,
NodeVariant(v) => v.node.name,
NodeField(f) => f.name,
- NodeLifetime(lt) => lt.name,
+ NodeLifetime(lt) => lt.name.name(),
NodeTyParam(tp) => tp.name,
NodeBinding(&Pat { node: PatKind::Binding(_,_,l,_), .. }) => l.node,
NodeStructCtor(_) => self.name(self.get_parent(id)),
Some(EntryVisibility(_, _, &Visibility::Restricted { ref path, .. })) => path.span,
Some(EntryVisibility(_, _, v)) => bug!("unexpected Visibility {:?}", v),
Some(EntryLocal(_, _, local)) => local.span,
+ Some(EntryMacroDef(_, macro_def)) => macro_def.span,
Some(RootCrate(_)) => self.forest.krate.span,
Some(NotPresent) | None => {
impl Named for TraitItem { fn name(&self) -> Name { self.name } }
impl Named for ImplItem { fn name(&self) -> Name { self.name } }
-pub fn map_crate<'hir>(forest: &'hir mut Forest,
+pub fn map_crate<'hir>(sess: &::session::Session,
+ cstore: &::middle::cstore::CrateStore,
+ forest: &'hir mut Forest,
definitions: &'hir Definitions)
-> Map<'hir> {
let map = {
+ let hcx = ::ich::StableHashingContext::new(sess, &forest.krate, definitions, cstore);
+
let mut collector = NodeCollector::root(&forest.krate,
&forest.dep_graph,
- &definitions);
+ &definitions,
+ hcx);
intravisit::walk_crate(&mut collector, &forest.krate);
- collector.into_map()
+
+ let crate_disambiguator = sess.local_crate_disambiguator().as_str();
+ collector.finalize_and_compute_crate_hash(&crate_disambiguator)
};
if log_enabled!(::log::LogLevel::Debug) {
// printing.
NodeStructCtor(_) => bug!("cannot print isolated StructCtor"),
NodeLocal(a) => self.print_local_decl(&a),
+ NodeMacroDef(_) => bug!("cannot print MacroDef"),
}
}
}
Some(NodeVisibility(ref vis)) => {
format!("visibility {:?}{}", vis, id_str)
}
+ Some(NodeMacroDef(_)) => {
+ format!("macro {}{}", path_str(), id_str)
+ }
None => {
format!("unknown node{}", id_str)
}
/// HIR lowering inserts these placeholders in type paths that
/// refer to type definitions needing lifetime parameters,
/// `&T` and `&mut T`, and trait objects without `... + 'a`.
- pub name: Name,
+ pub name: LifetimeName,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Copy)]
+pub enum LifetimeName {
+ Implicit,
+ Underscore,
+ Static,
+ Name(Name),
+}
+
+impl LifetimeName {
+ pub fn name(&self) -> Name {
+ use self::LifetimeName::*;
+ match *self {
+ Implicit => keywords::Invalid.name(),
+ Underscore => Symbol::intern("'_"),
+ Static => keywords::StaticLifetime.name(),
+ Name(name) => name,
+ }
+ }
}
impl fmt::Debug for Lifetime {
impl Lifetime {
pub fn is_elided(&self) -> bool {
- self.name == keywords::Invalid.name()
+ use self::LifetimeName::*;
+ match self.name {
+ Implicit | Underscore => true,
+ Static | Name(_) => false,
+ }
}
pub fn is_static(&self) -> bool {
- self.name == "'static"
+ self.name == LifetimeName::Static
}
}
self.pclose()?;
}
hir::ExprYield(ref expr) => {
- self.s.word("yield")?;
+ self.word_space("yield")?;
self.print_expr_maybe_paren(&expr, parser::PREC_JUMP)?;
}
}
}
pub fn print_lifetime(&mut self, lifetime: &hir::Lifetime) -> io::Result<()> {
- self.print_name(lifetime.name)
+ self.print_name(lifetime.name.name())
}
pub fn print_lifetime_def(&mut self, lifetime: &hir::LifetimeDef) -> io::Result<()> {
file_index: usize,
}
+#[derive(Clone)]
pub struct CachingCodemapView<'cm> {
codemap: &'cm CodeMap,
line_cache: [CacheEntry; 3],
/// enough information to transform DefIds and HirIds into stable DefPaths (i.e.
/// a reference to the TyCtxt) and it holds a few caches for speeding up various
/// things (e.g. each DefId/DefPath is only hashed once).
+#[derive(Clone)]
pub struct StableHashingContext<'gcx> {
sess: &'gcx Session,
definitions: &'gcx Definitions,
self.definitions.def_path_hash(def_index)
}
+ #[inline]
+ pub fn node_to_hir_id(&self, node_id: ast::NodeId) -> hir::HirId {
+ self.definitions.node_to_hir_id(node_id)
+ }
+
#[inline]
pub fn hash_spans(&self) -> bool {
self.hash_spans
}
}
+
+impl<'gcx> StableHashingContextProvider for StableHashingContext<'gcx> {
+ type ContextType = StableHashingContext<'gcx>;
+ fn create_stable_hashing_context(&self) -> Self::ContextType {
+ self.clone()
+ }
+}
+
+impl<'gcx> ::dep_graph::DepGraphSafe for StableHashingContext<'gcx> {
+}
+
+
impl<'gcx> HashStable<StableHashingContext<'gcx>> for hir::BodyId {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'gcx>,
}
}
+impl_stable_hash_for!(enum hir::LifetimeName {
+ Implicit,
+ Underscore,
+ Static,
+ Name(name)
+});
+
impl_stable_hash_for!(struct hir::Lifetime {
id,
span,
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let hir::TraitItem {
- id,
+ id: _,
hir_id: _,
name,
ref attrs,
} = *self;
hcx.hash_hir_item_like(attrs, |hcx| {
- id.hash_stable(hcx, hasher);
name.hash_stable(hcx, hasher);
attrs.hash_stable(hcx, hasher);
node.hash_stable(hcx, hasher);
hcx: &mut StableHashingContext<'gcx>,
hasher: &mut StableHasher<W>) {
let hir::ImplItem {
- id,
+ id: _,
hir_id: _,
name,
ref vis,
} = *self;
hcx.hash_hir_item_like(attrs, |hcx| {
- id.hash_stable(hcx, hasher);
name.hash_stable(hcx, hasher);
vis.hash_stable(hcx, hasher);
defaultness.hash_stable(hcx, hasher);
}
}
+impl<'gcx> ToStableHashKey<StableHashingContext<'gcx>> for hir::TraitCandidate {
+ type KeyType = (DefPathHash, Option<(DefPathHash, hir::ItemLocalId)>);
+
+ fn to_stable_hash_key(&self,
+ hcx: &StableHashingContext<'gcx>)
+ -> Self::KeyType {
+ let hir::TraitCandidate {
+ def_id,
+ import_id,
+ } = *self;
+
+ let import_id = import_id.map(|node_id| hcx.node_to_hir_id(node_id))
+ .map(|hir_id| (hcx.local_def_path_hash(hir_id.owner),
+ hir_id.local_id));
+ (hcx.def_path_hash(def_id), import_id)
+ }
+}
+
+
impl_stable_hash_for!(struct hir::Freevar {
def,
span
EqTraitLangItem, "eq", eq_trait;
OrdTraitLangItem, "ord", ord_trait;
- StrEqFnLangItem, "str_eq", str_eq_fn;
-
// A number of panic-related lang items. The `panic` item corresponds to
// divide-by-zero and various panic cases with `match`. The
// `panic_bounds_check` item is for indexing arrays.
hir_map::NodeVariant(_) |
hir_map::NodeStructCtor(_) |
hir_map::NodeField(_) |
- hir_map::NodeTy(_) => {}
+ hir_map::NodeTy(_) |
+ hir_map::NodeMacroDef(_) => {}
_ => {
bug!("found unexpected thingy in worklist: {}",
self.tcx.hir.node_to_string(search_item))
use util::nodemap::{FxHashMap, FxHashSet};
use ty;
-use std::collections::hash_map::Entry;
use std::mem;
use std::rc::Rc;
use syntax::codemap;
closure_tree: FxHashMap<hir::ItemLocalId, hir::ItemLocalId>,
/// If there are any `yield` nested within a scope, this map
- /// stores the `Span` of the first one.
- yield_in_scope: FxHashMap<Scope, Span>,
+ /// stores the `Span` of the last one and its index in the
+ /// postorder of the Visitor traversal on the HIR.
+ ///
+ /// HIR Visitor postorder indexes might seem like a peculiar
+ /// thing to care about. but it turns out that HIR bindings
+ /// and the temporary results of HIR expressions are never
+ /// storage-live at the end of HIR nodes with postorder indexes
+ /// lower than theirs, and therefore don't need to be suspended
+ /// at yield-points at these indexes.
+ ///
+ /// For an example, suppose we have some code such as:
+ /// ```rust,ignore (example)
+ /// foo(f(), yield y, bar(g()))
+ /// ```
+ ///
+ /// With the HIR tree (calls numbered for expository purposes)
+ /// ```
+ /// Call#0(foo, [Call#1(f), Yield(y), Call#2(bar, Call#3(g))])
+ /// ```
+ ///
+ /// Obviously, the result of `f()` was created before the yield
+ /// (and therefore needs to be kept valid over the yield) while
+ /// the result of `g()` occurs after the yield (and therefore
+ /// doesn't). If we want to infer that, we can look at the
+ /// postorder traversal:
+ /// ```
+ /// `foo` `f` Call#1 `y` Yield `bar` `g` Call#3 Call#2 Call#0
+ /// ```
+ ///
+ /// In which we can easily see that `Call#1` occurs before the yield,
+ /// and `Call#3` after it.
+ ///
+ /// To see that this method works, consider:
+ ///
+ /// Let `D` be our binding/temporary and `U` be our other HIR node, with
+ /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be
+ /// the yield and D would be one of the calls). Let's show that
+ /// `D` is storage-dead at `U`.
+ ///
+ /// Remember that storage-live/storage-dead refers to the state of
+ /// the *storage*, and does not consider moves/drop flags.
+ ///
+ /// Then:
+ /// 1. From the ordering guarantee of HIR visitors (see
+ /// `rustc::hir::intravisit`), `D` does not dominate `U`.
+ /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because
+ /// we might visit `U` without ever getting to `D`).
+ /// 3. However, we guarantee that at each HIR point, each
+ /// binding/temporary is always either always storage-live
+ /// or always storage-dead. This is what is being guaranteed
+ /// by `terminating_scopes` including all blocks where the
+ /// count of executions is not guaranteed.
+ /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`,
+ /// QED.
+ ///
+ /// I don't think this property relies on `3.` in an essential way - it
+ /// is probably still correct even if we have "unrestricted" terminating
+ /// scopes. However, why use the complicated proof when a simple one
+ /// works?
+ ///
+ /// A subtle thing: `box` expressions, such as `box (&x, yield 2, &y)`. It
+ /// might seem that a `box` expression creates a `Box<T>` temporary
+ /// when it *starts* executing, at `HIR-preorder(BOX-EXPR)`. That might
+ /// be true in the MIR desugaring, but it is not important in the semantics.
+ ///
+ /// The reason is that semantically, until the `box` expression returns,
+ /// the values are still owned by their containing expressions. So
+ /// we'll see that `&x`.
+ yield_in_scope: FxHashMap<Scope, (Span, usize)>,
+
+ /// The number of visit_expr and visit_pat calls done in the body.
+ /// Used to sanity check visit_expr/visit_pat call count when
+ /// calculating geneartor interiors.
+ body_expr_count: FxHashMap<hir::BodyId, usize>,
}
#[derive(Debug, Copy, Clone)]
struct RegionResolutionVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ // The number of expressions and patterns visited in the current body
+ expr_and_pat_count: usize,
+
// Generated scope tree:
scope_tree: ScopeTree,
}
/// Checks whether the given scope contains a `yield`. If so,
- /// returns `Some(span)` with the span of a yield we found.
- pub fn yield_in_scope(&self, scope: Scope) -> Option<Span> {
+ /// returns `Some((span, expr_count))` with the span of a yield we found and
+ /// the number of expressions appearing before the `yield` in the body.
+ pub fn yield_in_scope(&self, scope: Scope) -> Option<(Span, usize)> {
self.yield_in_scope.get(&scope).cloned()
}
+
+ /// Gives the number of expressions visited in a body.
+ /// Used to sanity check visit_expr call count when
+ /// calculating geneartor interiors.
+ pub fn body_expr_count(&self, body_id: hir::BodyId) -> Option<usize> {
+ self.body_expr_count.get(&body_id).map(|r| *r)
+ }
}
/// Records the lifetime of a local variable as `cx.var_parent`
}
intravisit::walk_pat(visitor, pat);
+
+ visitor.expr_and_pat_count += 1;
}
fn resolve_stmt<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
// record_superlifetime(new_cx, expr.callee_id);
}
- hir::ExprYield(..) => {
- // Mark this expr's scope and all parent scopes as containing `yield`.
- let mut scope = Scope::Node(expr.hir_id.local_id);
- loop {
- match visitor.scope_tree.yield_in_scope.entry(scope) {
- // Another `yield` has already been found.
- Entry::Occupied(_) => break,
-
- Entry::Vacant(entry) => {
- entry.insert(expr.span);
- }
- }
-
- // Keep traversing up while we can.
- match visitor.scope_tree.parent_map.get(&scope) {
- // Don't cross from closure bodies to their parent.
- Some(&Scope::CallSite(_)) => break,
- Some(&superscope) => scope = superscope,
- None => break
- }
- }
- }
-
_ => {}
}
}
_ => intravisit::walk_expr(visitor, expr)
}
+ visitor.expr_and_pat_count += 1;
+
+ if let hir::ExprYield(..) = expr.node {
+ // Mark this expr's scope and all parent scopes as containing `yield`.
+ let mut scope = Scope::Node(expr.hir_id.local_id);
+ loop {
+ visitor.scope_tree.yield_in_scope.insert(scope,
+ (expr.span, visitor.expr_and_pat_count));
+
+ // Keep traversing up while we can.
+ match visitor.scope_tree.parent_map.get(&scope) {
+ // Don't cross from closure bodies to their parent.
+ Some(&Scope::CallSite(_)) => break,
+ Some(&superscope) => scope = superscope,
+ None => break
+ }
+ }
+ }
+
visitor.cx = prev_cx;
}
body_id,
self.cx.parent);
+ let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0);
let outer_cx = self.cx;
let outer_ts = mem::replace(&mut self.terminating_scopes, FxHashSet());
self.terminating_scopes.insert(body.value.hir_id.local_id);
resolve_local(self, None, Some(&body.value));
}
+ if body.is_generator {
+ self.scope_tree.body_expr_count.insert(body_id, self.expr_and_pat_count);
+ }
+
// Restore context we had at the start.
+ self.expr_and_pat_count = outer_ec;
self.cx = outer_cx;
self.terminating_scopes = outer_ts;
}
let mut visitor = RegionResolutionVisitor {
tcx,
scope_tree: ScopeTree::default(),
+ expr_and_pat_count: 0,
cx: Context {
root_id: None,
parent: None,
let ScopeTree {
root_body,
root_parent,
+ ref body_expr_count,
ref parent_map,
ref var_map,
ref destruction_scopes,
root_parent.hash_stable(hcx, hasher);
});
+ body_expr_count.hash_stable(hcx, hasher);
parent_map.hash_stable(hcx, hasher);
var_map.hash_stable(hcx, hasher);
destruction_scopes.hash_stable(hcx, hasher);
}
impl Region {
- fn early(hir_map: &Map, index: &mut u32, def: &hir::LifetimeDef) -> (ast::Name, Region) {
+ fn early(hir_map: &Map, index: &mut u32, def: &hir::LifetimeDef)
+ -> (hir::LifetimeName, Region)
+ {
let i = *index;
*index += 1;
let def_id = hir_map.local_def_id(def.lifetime.id);
(def.lifetime.name, Region::EarlyBound(i, def_id))
}
- fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (ast::Name, Region) {
+ fn late(hir_map: &Map, def: &hir::LifetimeDef) -> (hir::LifetimeName, Region) {
let depth = ty::DebruijnIndex::new(1);
let def_id = hir_map.local_def_id(def.lifetime.id);
(def.lifetime.name, Region::LateBound(depth, def_id))
/// it should be shifted by the number of `Binder`s in between the
/// declaration `Binder` and the location it's referenced from.
Binder {
- lifetimes: FxHashMap<ast::Name, Region>,
+ lifetimes: FxHashMap<hir::LifetimeName, Region>,
s: ScopeRef<'a>
},
Scope::Binder { ref lifetimes, s } => {
// FIXME (#24278): non-hygienic comparison
- if let Some(def) = lifetimes.get(&label) {
+ if let Some(def) = lifetimes.get(&hir::LifetimeName::Name(label)) {
let node_id = hir_map.as_local_node_id(def.id().unwrap())
.unwrap();
Set1::Empty => "BaseDefault".to_string(),
Set1::One(Region::Static) => "'static".to_string(),
Set1::One(Region::EarlyBound(i, _)) => {
- generics.lifetimes[i as usize].lifetime.name.to_string()
+ generics.lifetimes[i as usize].lifetime.name.name().to_string()
}
Set1::One(_) => bug!(),
Set1::Many => "Ambiguous".to_string(),
/// for each type parameter.
fn object_lifetime_defaults_for_item(hir_map: &Map, generics: &hir::Generics)
-> Vec<ObjectLifetimeDefault> {
- fn add_bounds(set: &mut Set1<ast::Name>, bounds: &[hir::TyParamBound]) {
+ fn add_bounds(set: &mut Set1<hir::LifetimeName>, bounds: &[hir::TyParamBound]) {
for bound in bounds {
if let hir::RegionTyParamBound(ref lifetime) = *bound {
set.insert(lifetime.name);
match set {
Set1::Empty => Set1::Empty,
Set1::One(name) => {
- if name == "'static" {
+ if name == hir::LifetimeName::Static {
Set1::One(Region::Static)
} else {
generics.lifetimes.iter().enumerate().find(|&(_, def)| {
self.insert_lifetime(lifetime_ref, def);
} else {
struct_span_err!(self.sess, lifetime_ref.span, E0261,
- "use of undeclared lifetime name `{}`", lifetime_ref.name)
+ "use of undeclared lifetime name `{}`", lifetime_ref.name.name())
.span_label(lifetime_ref.span, "undeclared lifetime")
.emit();
}
let lifetime_i = &lifetimes[i];
for lifetime in lifetimes {
- if lifetime.lifetime.is_static() {
- let lifetime = lifetime.lifetime;
- let mut err = struct_span_err!(self.sess, lifetime.span, E0262,
- "invalid lifetime parameter name: `{}`", lifetime.name);
- err.span_label(lifetime.span,
- format!("{} is a reserved lifetime name", lifetime.name));
- err.emit();
+ match lifetime.lifetime.name {
+ hir::LifetimeName::Static | hir::LifetimeName::Underscore => {
+ let lifetime = lifetime.lifetime;
+ let name = lifetime.name.name();
+ let mut err = struct_span_err!(self.sess, lifetime.span, E0262,
+ "invalid lifetime parameter name: `{}`", name);
+ err.span_label(lifetime.span,
+ format!("{} is a reserved lifetime name", name));
+ err.emit();
+ }
+ hir::LifetimeName::Implicit | hir::LifetimeName::Name(_) => {}
}
}
if lifetime_i.lifetime.name == lifetime_j.lifetime.name {
struct_span_err!(self.sess, lifetime_j.lifetime.span, E0263,
"lifetime name `{}` declared twice in the same scope",
- lifetime_j.lifetime.name)
+ lifetime_j.lifetime.name.name())
.span_label(lifetime_j.lifetime.span,
"declared twice")
.span_label(lifetime_i.lifetime.span,
self.check_lifetime_def_for_shadowing(old_scope, &lifetime_i.lifetime);
for bound in &lifetime_i.bounds {
- if !bound.is_static() {
- self.resolve_lifetime_ref(bound);
- } else {
- self.insert_lifetime(bound, Region::Static);
- self.sess.struct_span_warn(lifetime_i.lifetime.span.to(bound.span),
- &format!("unnecessary lifetime parameter `{}`", lifetime_i.lifetime.name))
- .help(&format!("you can use the `'static` lifetime directly, in place \
- of `{}`", lifetime_i.lifetime.name))
- .emit();
+ match bound.name {
+ hir::LifetimeName::Underscore => {
+ let mut err = struct_span_err!(self.sess, bound.span, E0637,
+ "invalid lifetime bound name: `'_`");
+ err.span_label(bound.span, "`'_` is a reserved lifetime name");
+ err.emit();
+ }
+ hir::LifetimeName::Static => {
+ self.insert_lifetime(bound, Region::Static);
+ self.sess.struct_span_warn(lifetime_i.lifetime.span.to(bound.span),
+ &format!("unnecessary lifetime parameter `{}`",
+ lifetime_i.lifetime.name.name()))
+ .help(&format!(
+ "you can use the `'static` lifetime directly, in place \
+ of `{}`", lifetime_i.lifetime.name.name()))
+ .emit();
+ }
+ hir::LifetimeName::Implicit |
+ hir::LifetimeName::Name(_) => {
+ self.resolve_lifetime_ref(bound);
+ }
}
}
}
{
for &(label, label_span) in &self.labels_in_fn {
// FIXME (#24278): non-hygienic comparison
- if lifetime.name == label {
+ if lifetime.name.name() == label {
signal_shadowing_problem(self.sess,
- lifetime.name,
+ label,
original_label(label_span),
shadower_lifetime(&lifetime));
return;
signal_shadowing_problem(
self.sess,
- lifetime.name,
+ lifetime.name.name(),
original_lifetime(self.hir_map.span(node_id)),
shadower_lifetime(&lifetime));
return;
return;
struct ConstrainedCollector {
- regions: FxHashSet<ast::Name>,
+ regions: FxHashSet<hir::LifetimeName>,
}
impl<'v> Visitor<'v> for ConstrainedCollector {
}
struct AllCollector {
- regions: FxHashSet<ast::Name>,
+ regions: FxHashSet<hir::LifetimeName>,
impl_trait: bool
}
print on stdout",
"[crate-name|file-names|sysroot|cfg|target-list|\
target-cpus|target-features|relocation-models|\
- code-models|target-spec-json|native-static-deps]"),
+ code-models|target-spec-json|native-static-libs]"),
opt::flagmulti_s("g", "", "Equivalent to -C debuginfo=2"),
opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"),
opt::opt_s("o", "", "Write output to <filename>", "FILENAME"),
// FIXME: This is not general enough to make the warning lint completely override
// normal diagnostic warnings, since the warning lint can also be denied and changed
// later via the source code.
- let can_print_warnings = sopts.lint_opts
+ let warnings_allow = sopts.lint_opts
.iter()
.filter(|&&(ref key, _)| *key == "warnings")
- .map(|&(_, ref level)| *level != lint::Allow)
+ .map(|&(_, ref level)| *level == lint::Allow)
.last()
- .unwrap_or(true);
+ .unwrap_or(false);
+ let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow);
+
+ let can_print_warnings = !(warnings_allow || cap_lints_allow);
+
let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug;
let emitter: Box<Emitter> = match (sopts.error_format, emitter_dest) {
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
// For other types, we'll use the builtin rules.
- let copy_conditions = self.copy_conditions(obligation);
+ let copy_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?;
} else if lang_items.sized_trait() == Some(def_id) {
// Sized is never implementable by end-users, it is
// Same builtin conditions as `Copy`, i.e. every type which has builtin support
// for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone`
// types have builtin support for `Clone`.
- let clone_conditions = self.copy_conditions(obligation);
+ let clone_conditions = self.copy_clone_conditions(obligation);
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?;
}
}
}
- fn copy_conditions(&mut self, obligation: &TraitObligation<'tcx>)
+ fn copy_clone_conditions(&mut self, obligation: &TraitObligation<'tcx>)
-> BuiltinImplConditions<'tcx>
{
// NOTE: binder moved to (*)
Where(ty::Binder(Vec::new()))
}
- ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
- ty::TyClosure(..) | ty::TyGenerator(..) |
+ ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) | ty::TyGenerator(..) |
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
Never
}
Where(ty::Binder(tys.to_vec()))
}
+ ty::TyClosure(def_id, substs) => {
+ let trait_id = obligation.predicate.def_id();
+ let copy_closures =
+ Some(trait_id) == self.tcx().lang_items().copy_trait() &&
+ self.tcx().has_copy_closures(def_id.krate);
+ let clone_closures =
+ Some(trait_id) == self.tcx().lang_items().clone_trait() &&
+ self.tcx().has_clone_closures(def_id.krate);
+
+ if copy_closures || clone_closures {
+ Where(ty::Binder(substs.upvar_tys(def_id, self.tcx()).collect()))
+ } else {
+ Never
+ }
+ }
+
ty::TyAdt(..) | ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
// Fallback to whatever user-defined impls exist in this case.
None
self.sized_conditions(obligation)
}
_ if Some(trait_def) == lang_items.copy_trait() => {
- self.copy_conditions(obligation)
+ self.copy_clone_conditions(obligation)
}
_ if Some(trait_def) == lang_items.clone_trait() => {
- self.copy_conditions(obligation)
+ self.copy_clone_conditions(obligation)
}
_ => bug!("unexpected builtin trait {:?}", trait_def)
};
use util::nodemap::{FxHashMap, FxHashSet};
use rustc_data_structures::accumulate_vec::AccumulateVec;
use rustc_data_structures::stable_hasher::{HashStable, hash_stable_hashmap,
- StableHasher, StableHasherResult};
-
+ StableHasher, StableHasherResult,
+ StableVec};
use arena::{TypedArena, DroplessArena};
use rustc_const_math::{ConstInt, ConstUsize};
use rustc_data_structures::indexed_vec::IndexVec;
/// Map indicating what traits are in scope for places where this
/// is relevant; generated by resolve.
- trait_map: FxHashMap<DefIndex, Rc<FxHashMap<ItemLocalId, Rc<Vec<TraitCandidate>>>>>,
+ trait_map: FxHashMap<DefIndex,
+ Rc<FxHashMap<ItemLocalId,
+ Rc<StableVec<TraitCandidate>>>>>,
/// Export map produced by name resolution.
export_map: FxHashMap<DefId, Rc<Vec<Export>>>,
None
};
- // FIXME(mw): Each of the Vecs in the trait_map should be brought into
- // a deterministic order here. Otherwise we might end up with
- // unnecessarily unstable incr. comp. hashes.
let mut trait_map = FxHashMap();
for (k, v) in resolutions.trait_map {
let hir_id = hir.node_to_hir_id(k);
let map = trait_map.entry(hir_id.owner)
.or_insert_with(|| Rc::new(FxHashMap()));
- Rc::get_mut(map).unwrap().insert(hir_id.local_id, Rc::new(v));
+ Rc::get_mut(map).unwrap()
+ .insert(hir_id.local_id,
+ Rc::new(StableVec::new(v)));
}
let mut defs = FxHashMap();
for (k, v) in named_region_map.defs {
self.hir.definitions(),
self.cstore)
}
+
+ // This method exercises the `in_scope_traits_map` query for all possible
+ // values so that we have their fingerprints available in the DepGraph.
+ // This is only required as long as we still use the old dependency tracking
+ // which needs to have the fingerprints of all input nodes beforehand.
+ pub fn precompute_in_scope_traits_hashes(self) {
+ for &def_index in self.trait_map.keys() {
+ self.in_scope_traits_map(def_index);
+ }
+ }
}
impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> {
lint::struct_lint_level(self.sess, lint, level, src, None, msg)
}
- pub fn in_scope_traits(self, id: HirId) -> Option<Rc<Vec<TraitCandidate>>> {
+ pub fn in_scope_traits(self, id: HirId) -> Option<Rc<StableVec<TraitCandidate>>> {
self.in_scope_traits_map(id.owner)
.and_then(|map| map.get(&id.local_id).cloned())
}
assert_eq!(cnum, LOCAL_CRATE);
tcx.output_filenames.clone()
};
+ providers.has_copy_closures = |tcx, cnum| {
+ assert_eq!(cnum, LOCAL_CRATE);
+ tcx.sess.features.borrow().copy_closures
+ };
+ providers.has_clone_closures = |tcx, cnum| {
+ assert_eq!(cnum, LOCAL_CRATE);
+ tcx.sess.features.borrow().clone_closures
+ };
}
/// drop_in_place::<T>; None for empty drop glue.
DropGlue(DefId, Option<Ty<'tcx>>),
- /// Builtin method implementation, e.g. `Clone::clone`.
+ ///`<T as Clone>::clone` shim.
CloneShim(DefId, Ty<'tcx>),
}
format!("output_filenames")
}
}
+
+impl<'tcx> QueryDescription for queries::has_clone_closures<'tcx> {
+ fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
+ format!("seeing if the crate has enabled `Clone` closures")
+ }
+}
+
+impl<'tcx> QueryDescription for queries::has_copy_closures<'tcx> {
+ fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
+ format!("seeing if the crate has enabled `Copy` closures")
+ }
+}
use rustc_back::PanicStrategy;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::stable_hasher::StableVec;
use std::cell::{RefCell, Cell};
use std::ops::Deref;
[] fn specializes: specializes_node((DefId, DefId)) -> bool,
[] fn in_scope_traits_map: InScopeTraits(DefIndex)
- -> Option<Rc<FxHashMap<ItemLocalId, Rc<Vec<TraitCandidate>>>>>,
+ -> Option<Rc<FxHashMap<ItemLocalId, Rc<StableVec<TraitCandidate>>>>>,
[] fn module_exports: ModuleExports(DefId) -> Option<Rc<Vec<Export>>>,
[] fn lint_levels: lint_levels_node(CrateNum) -> Rc<lint::LintLevelMap>,
[] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats,
[] fn output_filenames: output_filenames_node(CrateNum)
-> Arc<OutputFilenames>,
+
+ [] fn has_copy_closures: HasCopyClosures(CrateNum) -> bool,
+ [] fn has_clone_closures: HasCloneClosures(CrateNum) -> bool,
}
//////////////////////////////////////////////////////////////////////
None
};
- if let Some(yield_span) = maybe_borrow_across_yield {
+ if let Some((yield_span, _)) = maybe_borrow_across_yield {
debug!("err_out_of_scope: opt_yield_span = {:?}", yield_span);
struct_span_err!(self.tcx.sess,
error_span,
entries.hash_stable(hcx, hasher);
}
+
+/// A vector container that makes sure that its items are hashed in a stable
+/// order.
+pub struct StableVec<T>(Vec<T>);
+
+impl<T> StableVec<T> {
+ pub fn new(v: Vec<T>) -> Self {
+ StableVec(v)
+ }
+}
+
+impl<T> ::std::ops::Deref for StableVec<T> {
+ type Target = Vec<T>;
+
+ fn deref(&self) -> &Vec<T> {
+ &self.0
+ }
+}
+
+impl<T, HCX> HashStable<HCX> for StableVec<T>
+ where T: HashStable<HCX> + ToStableHashKey<HCX>
+{
+ fn hash_stable<W: StableHasherResult>(&self,
+ hcx: &mut HCX,
+ hasher: &mut StableHasher<W>) {
+ let StableVec(ref v) = *self;
+
+ let mut sorted: Vec<_> = v.iter()
+ .map(|x| x.to_stable_hash_key(hcx))
+ .collect();
+ sorted.sort_unstable();
+ sorted.hash_stable(hcx, hasher);
+ }
+}
use rustc::util::common::{ErrorReported, time};
use rustc_allocator as allocator;
use rustc_borrowck as borrowck;
-use rustc_incremental::{self, IncrementalHashesMap};
+use rustc_incremental;
use rustc_resolve::{MakeGlobMap, Resolver};
use rustc_metadata::creader::CrateLoader;
use rustc_metadata::cstore::{self, CStore};
// Construct the HIR map
let hir_map = time(sess.time_passes(),
"indexing hir",
- || hir_map::map_crate(&mut hir_forest, &defs));
+ || hir_map::map_crate(sess, cstore, &mut hir_forest, &defs));
{
let _ignore = hir_map.dep_graph.in_ignore();
&arenas,
&crate_name,
&outputs,
- |tcx, analysis, incremental_hashes_map, rx, result| {
+ |tcx, analysis, rx, result| {
{
// Eventually, we will want to track plugins.
let _ignore = tcx.dep_graph.in_ignore();
tcx.print_debug_stats();
}
- let trans = phase_4_translate_to_llvm(tcx,
- incremental_hashes_map,
- rx);
+ let trans = phase_4_translate_to_llvm(tcx, rx);
if log_enabled!(::log::LogLevel::Info) {
println!("Post-trans");
-> Result<R, CompileIncomplete>
where F: for<'a> FnOnce(TyCtxt<'a, 'tcx, 'tcx>,
ty::CrateAnalysis,
- IncrementalHashesMap,
mpsc::Receiver<Box<Any + Send>>,
CompileResult) -> R
{
tx,
output_filenames,
|tcx| {
- let incremental_hashes_map =
- time(time_passes,
- "compute_incremental_hashes_map",
- || rustc_incremental::compute_incremental_hashes_map(tcx));
-
time(time_passes,
"load_dep_graph",
- || rustc_incremental::load_dep_graph(tcx, &incremental_hashes_map));
+ || rustc_incremental::load_dep_graph(tcx));
time(time_passes,
"stability checking",
|| stability::check_unstable_api_usage(tcx));
// passes are timed inside typeck
- try_with_f!(typeck::check_crate(tcx),
- (tcx, analysis, incremental_hashes_map, rx));
+ try_with_f!(typeck::check_crate(tcx), (tcx, analysis, rx));
time(time_passes,
"const checking",
// lint warnings and so on -- kindck used to do this abort, but
// kindck is gone now). -nmatsakis
if sess.err_count() > 0 {
- return Ok(f(tcx, analysis, incremental_hashes_map, rx, sess.compile_status()));
+ return Ok(f(tcx, analysis, rx, sess.compile_status()));
}
time(time_passes, "death checking", || middle::dead::check_crate(tcx));
time(time_passes, "lint checking", || lint::check_crate(tcx));
- return Ok(f(tcx, analysis, incremental_hashes_map, rx, tcx.sess.compile_status()));
+ return Ok(f(tcx, analysis, rx, tcx.sess.compile_status()));
})
}
/// Run the translation phase to LLVM, after which the AST and analysis can
/// be discarded.
pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: IncrementalHashesMap,
rx: mpsc::Receiver<Box<Any + Send>>)
-> write::OngoingCrateTranslation {
let time_passes = tcx.sess.time_passes();
let translation =
time(time_passes, "translation", move || {
- trans::trans_crate(tcx, incremental_hashes_map, rx)
+ trans::trans_crate(tcx, rx)
});
if tcx.sess.profile_queries() {
arenas,
id,
output_filenames,
- |tcx, _, _, _, _| {
+ |tcx, _, _, _| {
let empty_tables = ty::TypeckTables::empty(None);
let annotation = TypedAnnotation {
tcx,
arenas,
crate_name,
output_filenames,
- |tcx, _, _, _, _| {
+ |tcx, _, _, _| {
match ppm {
PpmMir | PpmMirCFG => {
if let Some(nodeid) = nodeid {
let arena = DroplessArena::new();
let arenas = ty::GlobalArenas::new();
- let hir_map = hir_map::map_crate(&mut hir_forest, &defs);
+ let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
// run just enough stuff to build a tcx:
let named_region_map = resolve_lifetime::krate(&sess, &*cstore, &hir_map);
+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <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.
-
-//! Calculation of the (misnamed) "strict version hash" for crates and
-//! items. This hash is used to tell when the HIR changed in such a
-//! way that results from previous compilations may no longer be
-//! applicable and hence must be recomputed. It should probably be
-//! renamed to the ICH (incremental compilation hash).
-//!
-//! The hashes for all items are computed once at the beginning of
-//! compilation and stored into a map. In addition, a hash is computed
-//! of the **entire crate**.
-//!
-//! Storing the hashes in a map avoids the need to compute them twice
-//! (once when loading prior incremental results and once when
-//! saving), but it is also important for correctness: at least as of
-//! the time of this writing, the typeck passes rewrites entries in
-//! the dep-map in-place to accommodate UFCS resolutions. Since name
-//! resolution is part of the hash, the result is that hashes computed
-//! at the end of compilation would be different from those computed
-//! at the beginning.
-
-use std::cell::RefCell;
-use std::hash::Hash;
-use rustc::dep_graph::{DepNode, DepKind};
-use rustc::hir;
-use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId, DefIndex};
-use rustc::hir::map::DefPathHash;
-use rustc::hir::itemlikevisit::ItemLikeVisitor;
-use rustc::ich::{Fingerprint, StableHashingContext};
-use rustc::ty::TyCtxt;
-use rustc::util::common::record_time;
-use rustc_data_structures::stable_hasher::{StableHasher, HashStable};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::accumulate_vec::AccumulateVec;
-
-pub type IchHasher = StableHasher<Fingerprint>;
-
-pub struct IncrementalHashesMap {
- hashes: FxHashMap<DepNode, Fingerprint>,
-
- // These are the metadata hashes for the current crate as they were stored
- // during the last compilation session. They are only loaded if
- // -Z query-dep-graph was specified and are needed for auto-tests using
- // the #[rustc_metadata_dirty] and #[rustc_metadata_clean] attributes to
- // check whether some metadata hash has changed in between two revisions.
- pub prev_metadata_hashes: RefCell<FxHashMap<DefId, Fingerprint>>,
-}
-
-impl IncrementalHashesMap {
- pub fn new() -> IncrementalHashesMap {
- IncrementalHashesMap {
- hashes: FxHashMap(),
- prev_metadata_hashes: RefCell::new(FxHashMap()),
- }
- }
-
- pub fn insert(&mut self, k: DepNode, v: Fingerprint) {
- assert!(self.hashes.insert(k, v).is_none());
- }
-
- pub fn iter<'a>(&'a self)
- -> ::std::collections::hash_map::Iter<'a, DepNode, Fingerprint> {
- self.hashes.iter()
- }
-
- pub fn len(&self) -> usize {
- self.hashes.len()
- }
-}
-
-impl<'a> ::std::ops::Index<&'a DepNode> for IncrementalHashesMap {
- type Output = Fingerprint;
-
- fn index(&self, index: &'a DepNode) -> &Fingerprint {
- match self.hashes.get(index) {
- Some(fingerprint) => fingerprint,
- None => {
- bug!("Could not find ICH for {:?}", index);
- }
- }
- }
-}
-
-struct ComputeItemHashesVisitor<'a, 'tcx: 'a> {
- tcx: TyCtxt<'a, 'tcx, 'tcx>,
- hcx: StableHashingContext<'tcx>,
- hashes: IncrementalHashesMap,
-}
-
-impl<'a, 'tcx: 'a> ComputeItemHashesVisitor<'a, 'tcx> {
- fn compute_and_store_ich_for_item_like<T>(&mut self,
- def_index: DefIndex,
- hash_bodies: bool,
- item_like: T)
- where T: HashStable<StableHashingContext<'tcx>>
- {
- if !hash_bodies && !self.tcx.sess.opts.build_dep_graph() {
- // If we just need the hashes in order to compute the SVH, we don't
- // need have two hashes per item. Just the one containing also the
- // item's body is sufficient.
- return
- }
-
- let def_path_hash = self.hcx.local_def_path_hash(def_index);
-
- let mut hasher = IchHasher::new();
- self.hcx.while_hashing_hir_bodies(hash_bodies, |hcx| {
- item_like.hash_stable(hcx, &mut hasher);
- });
-
- let bytes_hashed = hasher.bytes_hashed();
- let item_hash = hasher.finish();
- let dep_node = if hash_bodies {
- def_path_hash.to_dep_node(DepKind::HirBody)
- } else {
- def_path_hash.to_dep_node(DepKind::Hir)
- };
- debug!("calculate_def_hash: dep_node={:?} hash={:?}", dep_node, item_hash);
- self.hashes.insert(dep_node, item_hash);
-
- let bytes_hashed =
- self.tcx.sess.perf_stats.incr_comp_bytes_hashed.get() + bytes_hashed;
- self.tcx.sess.perf_stats.incr_comp_bytes_hashed.set(bytes_hashed);
-
- if hash_bodies {
- let in_scope_traits_map = self.tcx.in_scope_traits_map(def_index);
- let mut hasher = IchHasher::new();
- in_scope_traits_map.hash_stable(&mut self.hcx, &mut hasher);
- let dep_node = def_path_hash.to_dep_node(DepKind::InScopeTraits);
- self.hashes.insert(dep_node, hasher.finish());
- }
- }
-
- fn compute_crate_hash(&mut self) {
- let krate = self.tcx.hir.krate();
-
- let mut crate_state = IchHasher::new();
-
- let crate_disambiguator = self.tcx.sess.local_crate_disambiguator();
- "crate_disambiguator".hash(&mut crate_state);
- crate_disambiguator.as_str().len().hash(&mut crate_state);
- crate_disambiguator.as_str().hash(&mut crate_state);
-
- // add each item (in some deterministic order) to the overall
- // crate hash.
- {
- let mut item_hashes: Vec<_> =
- self.hashes.iter()
- .filter_map(|(&item_dep_node, &item_hash)| {
- // This `match` determines what kinds of nodes
- // go into the SVH:
- match item_dep_node.kind {
- DepKind::InScopeTraits |
- DepKind::Hir |
- DepKind::HirBody => {
- // We want to incoporate these into the
- // SVH.
- }
- DepKind::AllLocalTraitImpls => {
- // These are already covered by hashing
- // the HIR.
- return None
- }
- ref other => {
- bug!("Found unexpected DepKind during \
- SVH computation: {:?}",
- other)
- }
- }
-
- Some((item_dep_node, item_hash))
- })
- .collect();
- item_hashes.sort_unstable(); // avoid artificial dependencies on item ordering
- item_hashes.hash(&mut crate_state);
- }
-
- krate.attrs.hash_stable(&mut self.hcx, &mut crate_state);
-
- let crate_hash = crate_state.finish();
- self.hashes.insert(DepNode::new_no_params(DepKind::Krate), crate_hash);
- debug!("calculate_crate_hash: crate_hash={:?}", crate_hash);
- }
-
- fn hash_crate_root_module(&mut self, krate: &'tcx hir::Crate) {
- let hir::Crate {
- ref module,
- // Crate attributes are not copied over to the root `Mod`, so hash
- // them explicitly here.
- ref attrs,
- span,
-
- // These fields are handled separately:
- exported_macros: _,
- items: _,
- trait_items: _,
- impl_items: _,
- bodies: _,
- trait_impls: _,
- trait_default_impl: _,
- body_ids: _,
- } = *krate;
-
- self.compute_and_store_ich_for_item_like(CRATE_DEF_INDEX,
- false,
- (module, (span, attrs)));
- self.compute_and_store_ich_for_item_like(CRATE_DEF_INDEX,
- true,
- (module, (span, attrs)));
- }
-
- fn compute_and_store_ich_for_trait_impls(&mut self, krate: &'tcx hir::Crate)
- {
- let tcx = self.tcx;
-
- let mut impls: Vec<(DefPathHash, Fingerprint)> = krate
- .trait_impls
- .iter()
- .map(|(&trait_id, impls)| {
- let trait_id = tcx.def_path_hash(trait_id);
- let mut impls: AccumulateVec<[_; 32]> = impls
- .iter()
- .map(|&node_id| {
- let def_id = tcx.hir.local_def_id(node_id);
- tcx.def_path_hash(def_id)
- })
- .collect();
-
- impls.sort_unstable();
- let mut hasher = StableHasher::new();
- impls.hash_stable(&mut self.hcx, &mut hasher);
- (trait_id, hasher.finish())
- })
- .collect();
-
- impls.sort_unstable();
-
- let mut default_impls: AccumulateVec<[_; 32]> = krate
- .trait_default_impl
- .iter()
- .map(|(&trait_def_id, &impl_node_id)| {
- let impl_def_id = tcx.hir.local_def_id(impl_node_id);
- (tcx.def_path_hash(trait_def_id), tcx.def_path_hash(impl_def_id))
- })
- .collect();
-
- default_impls.sort_unstable();
-
- let mut hasher = StableHasher::new();
- impls.hash_stable(&mut self.hcx, &mut hasher);
-
- self.hashes.insert(DepNode::new_no_params(DepKind::AllLocalTraitImpls),
- hasher.finish());
- }
-}
-
-impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for ComputeItemHashesVisitor<'a, 'tcx> {
- fn visit_item(&mut self, item: &'tcx hir::Item) {
- let def_index = self.tcx.hir.local_def_id(item.id).index;
- self.compute_and_store_ich_for_item_like(def_index,
- false,
- item);
- self.compute_and_store_ich_for_item_like(def_index,
- true,
- item);
- }
-
- fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem) {
- let def_index = self.tcx.hir.local_def_id(item.id).index;
- self.compute_and_store_ich_for_item_like(def_index,
- false,
- item);
- self.compute_and_store_ich_for_item_like(def_index,
- true,
- item);
- }
-
- fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem) {
- let def_index = self.tcx.hir.local_def_id(item.id).index;
- self.compute_and_store_ich_for_item_like(def_index,
- false,
- item);
- self.compute_and_store_ich_for_item_like(def_index,
- true,
- item);
- }
-}
-
-
-
-pub fn compute_incremental_hashes_map<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>)
- -> IncrementalHashesMap {
- let _ignore = tcx.dep_graph.in_ignore();
- let krate = tcx.hir.krate();
-
- let mut visitor = ComputeItemHashesVisitor {
- tcx,
- hcx: tcx.create_stable_hashing_context(),
- hashes: IncrementalHashesMap::new(),
- };
-
- record_time(&tcx.sess.perf_stats.incr_comp_hashes_time, || {
- visitor.hash_crate_root_module(krate);
- krate.visit_all_item_likes(&mut visitor);
-
- for macro_def in krate.exported_macros.iter() {
- let def_index = tcx.hir.local_def_id(macro_def.id).index;
- visitor.compute_and_store_ich_for_item_like(def_index,
- false,
- macro_def);
- visitor.compute_and_store_ich_for_item_like(def_index,
- true,
- macro_def);
- }
-
- visitor.compute_and_store_ich_for_trait_impls(krate);
- });
-
- tcx.sess.perf_stats.incr_comp_hashes_count.set(visitor.hashes.len() as u64);
-
- record_time(&tcx.sess.perf_stats.svh_time, || visitor.compute_crate_hash());
- visitor.hashes
-}
extern crate syntax_pos;
mod assert_dep_graph;
-mod calculate_svh;
mod persist;
pub use assert_dep_graph::assert_dep_graph;
-pub use calculate_svh::compute_incremental_hashes_map;
-pub use calculate_svh::IncrementalHashesMap;
-pub use calculate_svh::IchHasher;
pub use persist::load_dep_graph;
pub use persist::save_dep_graph;
pub use persist::save_trans_partition;
use rustc_serialize::Decodable;
use rustc_serialize::opaque::Decoder;
-use IncrementalHashesMap;
use super::data::*;
use super::fs::*;
use super::file_format;
pub struct HashContext<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: &'a IncrementalHashesMap,
metadata_hashes: FxHashMap<DefId, Fingerprint>,
crate_hashes: FxHashMap<CrateNum, Svh>,
}
impl<'a, 'tcx> HashContext<'a, 'tcx> {
- pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: &'a IncrementalHashesMap)
- -> Self {
+ pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
HashContext {
tcx,
- incremental_hashes_map,
metadata_hashes: FxHashMap(),
crate_hashes: FxHashMap(),
}
}
- pub fn is_hashable(tcx: TyCtxt, dep_node: &DepNode) -> bool {
- match dep_node.kind {
- DepKind::Krate |
- DepKind::Hir |
- DepKind::InScopeTraits |
- DepKind::HirBody =>
- true,
- DepKind::MetaData => {
- let def_id = dep_node.extract_def_id(tcx).unwrap();
- !def_id.is_local()
- }
- _ => false,
- }
- }
-
pub fn hash(&mut self, dep_node: &DepNode) -> Option<Fingerprint> {
match dep_node.kind {
- DepKind::Krate => {
- Some(self.incremental_hashes_map[dep_node])
- }
-
// HIR nodes (which always come from our crate) are an input:
+ DepKind::Krate |
DepKind::InScopeTraits |
DepKind::Hir |
DepKind::HirBody => {
- Some(self.incremental_hashes_map[dep_node])
+ Some(self.tcx.dep_graph.fingerprint_of(dep_node).unwrap())
}
// MetaData from other crates is an *input* to us.
// save it for others to use.
DepKind::MetaData => {
let def_id = dep_node.extract_def_id(self.tcx).unwrap();
- if !def_id.is_local() {
- Some(self.metadata_hash(def_id,
+ assert!(!def_id.is_local());
+
+ Some(self.metadata_hash(def_id,
def_id.krate,
|this| &mut this.metadata_hashes))
- } else {
- None
- }
}
_ => {
//! Code to save/load the dep-graph from files.
use rustc::dep_graph::{DepNode, WorkProductId, DepKind};
-use rustc::hir::def_id::DefId;
use rustc::hir::svh::Svh;
use rustc::ich::Fingerprint;
use rustc::session::Session;
use rustc::ty::TyCtxt;
+use rustc::util::nodemap::DefIdMap;
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_serialize::Decodable as RustcDecodable;
use rustc_serialize::opaque::Decoder;
use std::path::{Path};
-use IncrementalHashesMap;
use super::data::*;
use super::dirty_clean;
use super::hash::*;
/// early in compilation, before we've really done any work, but
/// actually it doesn't matter all that much.) See `README.md` for
/// more general overview.
-pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: &IncrementalHashesMap) {
+pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
+ tcx.precompute_in_scope_traits_hashes();
if tcx.sess.incr_session_load_dep_graph() {
let _ignore = tcx.dep_graph.in_ignore();
- load_dep_graph_if_exists(tcx, incremental_hashes_map);
+ load_dep_graph_if_exists(tcx);
}
}
-fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: &IncrementalHashesMap) {
+fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
let dep_graph_path = dep_graph_path(tcx.sess);
let dep_graph_data = match load_data(tcx.sess, &dep_graph_path) {
Some(p) => p,
None => return // no file
};
- match decode_dep_graph(tcx, incremental_hashes_map, &dep_graph_data, &work_products_data) {
+ match decode_dep_graph(tcx, &dep_graph_data, &work_products_data) {
Ok(dirty_nodes) => dirty_nodes,
Err(err) => {
tcx.sess.warn(
/// Decode the dep graph and load the edges/nodes that are still clean
/// into `tcx.dep_graph`.
pub fn decode_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: &IncrementalHashesMap,
dep_graph_data: &[u8],
work_products_data: &[u8])
-> Result<(), String>
// Compute the set of nodes from the old graph where some input
// has changed or been removed.
let dirty_raw_nodes = initial_dirty_nodes(tcx,
- incremental_hashes_map,
&serialized_dep_graph.nodes,
&serialized_dep_graph.hashes);
let dirty_raw_nodes = transitive_dirty_nodes(&serialized_dep_graph,
dirty_clean::check_dirty_clean_annotations(tcx,
&serialized_dep_graph.nodes,
&dirty_raw_nodes);
-
- load_prev_metadata_hashes(tcx,
- &mut *incremental_hashes_map.prev_metadata_hashes.borrow_mut());
Ok(())
}
/// Computes which of the original set of def-ids are dirty. Stored in
/// a bit vector where the index is the DefPathIndex.
fn initial_dirty_nodes<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: &IncrementalHashesMap,
nodes: &IndexVec<DepNodeIndex, DepNode>,
serialized_hashes: &[(DepNodeIndex, Fingerprint)])
-> DirtyNodes {
- let mut hcx = HashContext::new(tcx, incremental_hashes_map);
+ let mut hcx = HashContext::new(tcx);
let mut dirty_nodes = FxHashMap();
for &(dep_node_index, prev_hash) in serialized_hashes {
work_product::delete_workproduct_files(tcx.sess, &swp.work_product);
}
-fn load_prev_metadata_hashes(tcx: TyCtxt,
- output: &mut FxHashMap<DefId, Fingerprint>) {
+pub fn load_prev_metadata_hashes(tcx: TyCtxt) -> DefIdMap<Fingerprint> {
+ let mut output = DefIdMap();
+
if !tcx.sess.opts.debugging_opts.query_dep_graph {
// Previous metadata hashes are only needed for testing.
- return
+ return output
}
debug!("load_prev_metadata_hashes() - Loading previous metadata hashes");
if !file_path.exists() {
debug!("load_prev_metadata_hashes() - Couldn't find file containing \
hashes at `{}`", file_path.display());
- return
+ return output
}
debug!("load_prev_metadata_hashes() - File: {}", file_path.display());
Ok(None) => {
debug!("load_prev_metadata_hashes() - File produced by incompatible \
compiler version: {}", file_path.display());
- return
+ return output
}
Err(err) => {
debug!("load_prev_metadata_hashes() - Error reading file `{}`: {}",
file_path.display(), err);
- return
+ return output
}
};
debug!("load_prev_metadata_hashes() - successfully loaded {} hashes",
serialized_hashes.index_map.len());
+
+ output
}
fn process_edge<'a, 'tcx, 'edges>(
// Reduce the graph to the most important nodes.
let compress::Reduction { graph, input_nodes } =
compress::reduce_graph(&query.graph,
- |n| HashContext::is_hashable(tcx, n),
+ |n| n.kind.is_input(),
|n| is_output(n));
let mut hashes = FxHashMap();
use rustc::middle::cstore::EncodedMetadataHashes;
use rustc::session::Session;
use rustc::ty::TyCtxt;
+use rustc::util::nodemap::DefIdMap;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph;
use rustc_data_structures::indexed_vec::IndexVec;
use std::fs::{self, File};
use std::path::PathBuf;
-use IncrementalHashesMap;
use super::data::*;
use super::hash::*;
use super::preds::*;
use super::file_format;
use super::work_product;
+use super::load::load_prev_metadata_hashes;
+
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: IncrementalHashesMap,
metadata_hashes: &EncodedMetadataHashes,
svh: Svh) {
debug!("save_dep_graph()");
eprintln!("incremental: {} edges in dep-graph", query.graph.len_edges());
}
- let mut hcx = HashContext::new(tcx, &incremental_hashes_map);
+ // We load the previous metadata hashes now before overwriting the file
+ // (if we need them for testing).
+ let prev_metadata_hashes = if tcx.sess.opts.debugging_opts.query_dep_graph {
+ load_prev_metadata_hashes(tcx)
+ } else {
+ DefIdMap()
+ };
+
+ let mut hcx = HashContext::new(tcx);
let preds = Predecessors::new(&query, &mut hcx);
let mut current_metadata_hashes = FxHashMap();
dep_graph_path(sess),
|e| encode_dep_graph(tcx, &preds, e));
- let prev_metadata_hashes = incremental_hashes_map.prev_metadata_hashes.borrow();
dirty_clean::check_dirty_clean_metadata(tcx,
- &*prev_metadata_hashes,
+ &prev_metadata_hashes,
¤t_metadata_hashes);
}
fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) {
self.check_snake_case(cx,
"lifetime",
- &t.lifetime.name.as_str(),
+ &t.lifetime.name.name().as_str(),
Some(t.lifetime.span));
}
attr::contains_name(&attrs, "no_builtins")
}
+ pub fn has_copy_closures(&self) -> bool {
+ let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
+ attr::contains_feature_attr(&attrs, "copy_closures")
+ }
+
+ pub fn has_clone_closures(&self) -> bool {
+ let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
+ attr::contains_feature_attr(&attrs, "clone_closures")
+ }
+
pub fn panic_strategy(&self) -> PanicStrategy {
self.root.panic_strategy.clone()
}
}
used_crate_source => { Rc::new(cdata.source.clone()) }
+
+ has_copy_closures => { cdata.has_copy_closures() }
+ has_clone_closures => { cdata.has_clone_closures() }
}
pub fn provide_local<'tcx>(providers: &mut Providers<'tcx>) {
}
ExprKind::Box { value } => {
let value = this.hir.mirror(value);
- let result = this.local_decls.push(LocalDecl::new_temp(expr.ty, expr_span));
+ // The `Box<T>` temporary created here is not a part of the HIR,
+ // and therefore is not considered during generator OIBIT
+ // determination. See the comment about `box` at `yield_in_scope`.
+ let result = this.local_decls.push(
+ LocalDecl::new_internal(expr.ty, expr_span));
this.cfg.push(block, Statement {
source_info,
kind: StatementKind::StorageLive(result)
let len = len.val.to_const_int().unwrap().to_u64().unwrap();
builder.array_shim(ty, len)
}
- ty::TyTuple(tys, _) => builder.tuple_shim(tys),
+ ty::TyClosure(def_id, substs) => {
+ builder.tuple_like_shim(
+ &substs.upvar_tys(def_id, tcx).collect::<Vec<_>>(),
+ AggregateKind::Closure(def_id, substs)
+ )
+ }
+ ty::TyTuple(tys, _) => builder.tuple_like_shim(&**tys, AggregateKind::Tuple),
_ => {
- bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty);
+ bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
}
};
self.block(vec![], TerminatorKind::Resume, true);
}
- fn tuple_shim(&mut self, tys: &ty::Slice<Ty<'tcx>>) {
+ fn tuple_like_shim(&mut self, tys: &[ty::Ty<'tcx>], kind: AggregateKind<'tcx>) {
+ match kind {
+ AggregateKind::Tuple | AggregateKind::Closure(..) => (),
+ _ => bug!("only tuples and closures are accepted"),
+ };
+
let rcvr = Lvalue::Local(Local::new(1+0)).deref();
let mut returns = Vec::new();
}
}
- // `return (returns[0], returns[1], ..., returns[tys.len() - 1]);`
+ // `return kind(returns[0], returns[1], ..., returns[tys.len() - 1]);`
let ret_statement = self.make_statement(
StatementKind::Assign(
Lvalue::Local(RETURN_POINTER),
Rvalue::Aggregate(
- box AggregateKind::Tuple,
+ box kind,
returns.into_iter().map(Operand::Consume).collect()
)
)
);
- self.block(vec![ret_statement], TerminatorKind::Return, false);
+ self.block(vec![ret_statement], TerminatorKind::Return, false);
}
}
}
impl<'a> Visitor<'a> for AstValidator<'a> {
- fn visit_lifetime(&mut self, lt: &'a Lifetime) {
- if lt.ident.name == "'_" {
- self.err_handler().span_err(lt.span, &format!("invalid lifetime name `{}`", lt.ident));
- }
-
- visit::walk_lifetime(self, lt)
- }
-
fn visit_expr(&mut self, expr: &'a Expr) {
match expr.node {
ExprKind::While(.., Some(ident)) |
ItemKind::Use(..) => {
// don't suggest placing a use before the prelude
// import or other generated ones
- if item.span == DUMMY_SP {
+ if item.span.ctxt().outer().expn_info().is_none() {
self.span = Some(item.span.with_hi(item.span.lo()));
self.found_use = true;
return;
ItemKind::ExternCrate(_) => {}
// but place them before the first other item
_ => if self.span.map_or(true, |span| item.span < span ) {
- self.span = Some(item.span.with_hi(item.span.lo()));
+ if item.span.ctxt().outer().expn_info().is_none() {
+ // don't insert between attributes and an item
+ if item.attrs.is_empty() {
+ self.span = Some(item.span.with_hi(item.span.lo()));
+ } else {
+ // find the first attribute on the item
+ for attr in &item.attrs {
+ if self.span.map_or(true, |span| attr.span < span) {
+ self.span = Some(attr.span.with_hi(attr.span.lo()));
+ }
+ }
+ }
+ }
},
}
}
- assert!(self.span.is_some(), "a file can't have no items and emit suggestions");
}
}
};
visit::walk_crate(&mut finder, krate);
if !candidates.is_empty() {
- let span = finder.span.expect("did not find module");
- show_candidates(&mut err, span, &candidates, better, finder.found_use);
+ show_candidates(&mut err, finder.span, &candidates, better, finder.found_use);
}
err.emit();
}
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
fn show_candidates(err: &mut DiagnosticBuilder,
- span: Span,
+ // This is `None` if all placement locations are inside expansions
+ span: Option<Span>,
candidates: &[ImportSuggestion],
better: bool,
found_use: bool) {
};
let msg = format!("possible {}candidate{} into scope", better, msg_diff);
- for candidate in &mut path_strings {
- // produce an additional newline to separate the new use statement
- // from the directly following item.
- let additional_newline = if found_use {
- ""
- } else {
- "\n"
- };
- *candidate = format!("use {};\n{}", candidate, additional_newline);
- }
+ if let Some(span) = span {
+ for candidate in &mut path_strings {
+ // produce an additional newline to separate the new use statement
+ // from the directly following item.
+ let additional_newline = if found_use {
+ ""
+ } else {
+ "\n"
+ };
+ *candidate = format!("use {};\n{}", candidate, additional_newline);
+ }
- err.span_suggestions(span, &msg, path_strings);
+ err.span_suggestions(span, &msg, path_strings);
+ } else {
+ let mut msg = msg;
+ msg.push(':');
+ for candidate in path_strings {
+ msg.push('\n');
+ msg.push_str(&candidate);
+ }
+ }
}
/// A somewhat inefficient routine to obtain the name of a module.
use rustc::session::filesearch;
use rustc::session::search_paths::PathKind;
use rustc::session::Session;
+use rustc::ich::Fingerprint;
use rustc::middle::cstore::{LinkMeta, NativeLibrary, LibSource, NativeLibraryKind};
use rustc::middle::dependency_format::Linkage;
use {CrateTranslation, CrateInfo};
use rustc::util::common::time;
use rustc::util::fs::fix_windows_verbatim_for_gcc;
-use rustc::dep_graph::{DepKind, DepNode};
use rustc::hir::def_id::CrateNum;
use rustc::hir::svh::Svh;
use rustc_back::tempdir::TempDir;
use rustc_back::{PanicStrategy, RelroLevel};
-use rustc_incremental::IncrementalHashesMap;
use context::get_reloc_model;
use llvm;
pub use self::rustc_trans_utils::link::{find_crate_name, filename_for_input,
default_output_for_target, invalid_output_for_target};
-pub fn build_link_meta(incremental_hashes_map: &IncrementalHashesMap) -> LinkMeta {
- let krate_dep_node = &DepNode::new_no_params(DepKind::Krate);
+pub fn build_link_meta(crate_hash: Fingerprint) -> LinkMeta {
let r = LinkMeta {
- crate_hash: Svh::new(incremental_hashes_map[krate_dep_node].to_smaller_hash()),
+ crate_hash: Svh::new(crate_hash.to_smaller_hash()),
};
info!("{:?}", r);
return r;
let sysroot = self.sess.sysroot();
let natvis_dir_path = sysroot.join("lib\\rustlib\\etc");
if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) {
+ // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes
+ // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore
+ // them, eventually mooting this workaround, per this landed patch:
+ // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100
+ if let Some(ref linker_path) = self.sess.opts.cg.linker {
+ if let Some(linker_name) = Path::new(&linker_path).file_stem() {
+ if linker_name.to_str().unwrap().to_lowercase() == "lld-link" {
+ self.sess.warn("not embedding natvis: lld-link may not support the flag");
+ return;
+ }
+ }
+ }
for entry in natvis_dir {
match entry {
Ok(entry) => {
use rustc::middle::cstore::{EncodedMetadata, EncodedMetadataHashes};
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::maps::Providers;
+use rustc::dep_graph::{DepNode, DepKind};
use rustc::middle::cstore::{self, LinkMeta, LinkagePreference};
use rustc::hir::map as hir_map;
use rustc::util::common::{time, print_time_passes_entry};
use rustc::session::config::{self, NoDebugInfo};
use rustc::session::Session;
-use rustc_incremental::{self, IncrementalHashesMap};
+use rustc_incremental;
use abi;
use allocator;
use mir::lvalue::LvalueRef;
}
pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: IncrementalHashesMap,
rx: mpsc::Receiver<Box<Any + Send>>)
-> OngoingCrateTranslation {
check_for_rustc_errors_attr(tcx);
- let link_meta = link::build_link_meta(&incremental_hashes_map);
+
+ let crate_hash = tcx.dep_graph
+ .fingerprint_of(&DepNode::new_no_params(DepKind::Krate))
+ .unwrap();
+ let link_meta = link::build_link_meta(crate_hash);
let exported_symbol_node_ids = find_exported_symbols(tcx);
let shared_ccx = SharedCrateContext::new(tcx);
ongoing_translation.translation_finished(tcx);
assert_and_save_dep_graph(tcx,
- incremental_hashes_map,
metadata_incr_hashes,
link_meta);
ongoing_translation.check_for_errors(tcx.sess);
assert_and_save_dep_graph(tcx,
- incremental_hashes_map,
metadata_incr_hashes,
link_meta);
ongoing_translation
thread_local!(static DISPOSITIONS: RefCell<Vec<(String, Disposition)>> = Default::default());
fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- incremental_hashes_map: IncrementalHashesMap,
metadata_incr_hashes: EncodedMetadataHashes,
link_meta: LinkMeta) {
time(tcx.sess.time_passes(),
time(tcx.sess.time_passes(),
"serialize dep graph",
|| rustc_incremental::save_dep_graph(tcx,
- incremental_hashes_map,
&metadata_incr_hashes,
link_meta.crate_hash));
}
use rustc::hir::def_id::DefId;
use rustc::hir::map::DefPathData;
use rustc::middle::trans::{Linkage, Visibility};
+use rustc::ich::Fingerprint;
use rustc::session::config::NUMBERED_CODEGEN_UNIT_MARKER;
use rustc::ty::{self, TyCtxt, InstanceDef};
use rustc::ty::item_path::characteristic_def_id_of_type;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
-use rustc_incremental::IchHasher;
+use rustc_data_structures::stable_hasher::StableHasher;
use std::collections::hash_map::Entry;
use std::hash::Hash;
use syntax::ast::NodeId;
}
fn compute_symbol_name_hash<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> u64 {
- let mut state = IchHasher::new();
+ let mut state: StableHasher<Fingerprint> = StableHasher::new();
let all_items = self.items_in_deterministic_order(tcx);
for (item, (linkage, visibility)) in all_items {
let symbol_name = item.symbol_name(tcx);
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
-use rustc::hir::{self, Body, Pat, PatKind, Expr};
+use rustc::hir::{self, Pat, PatKind, Expr};
use rustc::middle::region;
use rustc::ty::Ty;
use std::rc::Rc;
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
types: FxHashMap<Ty<'tcx>, usize>,
region_scope_tree: Rc<region::ScopeTree>,
+ expr_count: usize,
}
impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> {
use syntax_pos::DUMMY_SP;
let live_across_yield = scope.map_or(Some(DUMMY_SP), |s| {
- self.region_scope_tree.yield_in_scope(s)
+ self.region_scope_tree.yield_in_scope(s).and_then(|(span, expr_count)| {
+ // If we are recording an expression that is the last yield
+ // in the scope, or that has a postorder CFG index larger
+ // than the one of all of the yields, then its value can't
+ // be storage-live (and therefore live) at any of the yields.
+ //
+ // See the mega-comment at `yield_in_scope` for a proof.
+ if expr_count >= self.expr_count {
+ Some(span)
+ } else {
+ None
+ }
+ })
});
if let Some(span) = live_across_yield {
fcx,
types: FxHashMap(),
region_scope_tree: fcx.tcx.region_scope_tree(def_id),
+ expr_count: 0,
};
intravisit::walk_body(&mut visitor, body);
+ // Check that we visited the same amount of expressions and the RegionResolutionVisitor
+ let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
+ assert_eq!(region_expr_count, visitor.expr_count);
+
let mut types: Vec<_> = visitor.types.drain().collect();
// Sort types by insertion order
}
}
+// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in
+// librustc/middle/region.rs since `expr_count` is compared against the results
+// there.
impl<'a, 'gcx, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'gcx, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
- fn visit_body(&mut self, _body: &'tcx Body) {
- // Closures inside are not considered part of the generator interior
- }
-
fn visit_pat(&mut self, pat: &'tcx Pat) {
if let PatKind::Binding(..) = pat.node {
let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id);
self.record(ty, Some(scope), None);
}
+ self.expr_count += 1;
+
intravisit::walk_pat(self, pat);
}
fn visit_expr(&mut self, expr: &'tcx Expr) {
+ intravisit::walk_expr(self, expr);
+
+ self.expr_count += 1;
+
let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
+
let ty = self.fcx.tables.borrow().expr_ty_adjusted(expr);
self.record(ty, scope, Some(expr));
-
- intravisit::walk_expr(self, expr);
}
}
let (span, name) = if index < ast_generics.lifetimes.len() {
(ast_generics.lifetimes[index].lifetime.span,
- ast_generics.lifetimes[index].lifetime.name)
+ ast_generics.lifetimes[index].lifetime.name.name())
} else {
let index = index - ast_generics.lifetimes.len();
(ast_generics.ty_params[index].span,
let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics);
let regions = early_lifetimes.enumerate().map(|(i, l)| {
ty::RegionParameterDef {
- name: l.lifetime.name,
+ name: l.lifetime.name.name(),
index: own_start + i as u32,
def_id: tcx.hir.local_def_id(l.lifetime.id),
pure_wrt_drop: l.pure_wrt_drop,
let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
def_id: tcx.hir.local_def_id(param.lifetime.id),
index,
- name: param.lifetime.name
+ name: param.lifetime.name.name(),
}));
index += 1;
!input_parameters.contains(¶m)
{
report_unused_parameter(tcx, lifetime.lifetime.span,
- "lifetime", &lifetime.lifetime.name.to_string());
+ "lifetime", &lifetime.lifetime.name.name().to_string());
}
}
}
_ => {}
}
- Lifetime(self.name.to_string())
+ Lifetime(self.name.name().to_string())
}
}
fn clean(&self, _: &DocContext) -> Lifetime {
if self.bounds.len() > 0 {
let mut s = format!("{}: {}",
- self.lifetime.name.to_string(),
- self.bounds[0].name.to_string());
+ self.lifetime.name.name(),
+ self.bounds[0].name.name());
for bound in self.bounds.iter().skip(1) {
- s.push_str(&format!(" + {}", bound.name.to_string()));
+ s.push_str(&format!(" + {}", bound.name.name()));
}
Lifetime(s)
} else {
- Lifetime(self.lifetime.name.to_string())
+ Lifetime(self.lifetime.name.name().to_string())
}
}
}
let arena = DroplessArena::new();
let arenas = GlobalArenas::new();
- let hir_map = hir_map::map_crate(&mut hir_forest, &defs);
+ let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
let output_filenames = driver::build_output_filenames(&input,
&None,
&None,
&arenas,
&name,
&output_filenames,
- |tcx, analysis, _, _, result| {
+ |tcx, analysis, _, result| {
if let Err(_) = result {
sess.fatal("Compilation failed, aborting rustdoc");
}
let markdown_warnings = scx.markdown_warnings.borrow();
if !markdown_warnings.is_empty() {
- println!("WARNING: documentation for this crate may be rendered \
- differently using the new Pulldown renderer.");
- println!(" See https://github.com/rust-lang/rust/issues/44229 for details.");
+ let mut intro_msg = false;
for &(ref span, ref text, ref diffs) in &*markdown_warnings {
- println!("WARNING: rendering difference in `{}`", concise_str(text));
- println!(" --> {}:{}:{}", span.filename, span.loline, span.locol);
for d in diffs {
- render_difference(d);
+ render_difference(d, &mut intro_msg, span, text);
}
}
}
(format!("...{}", concise_str(s1)), format!("...{}", concise_str(s2)))
}
-fn render_difference(diff: &html_diff::Difference) {
+
+fn print_message(msg: &str, intro_msg: &mut bool, span: &Span, text: &str) {
+ if !*intro_msg {
+ println!("WARNING: documentation for this crate may be rendered \
+ differently using the new Pulldown renderer.");
+ println!(" See https://github.com/rust-lang/rust/issues/44229 for details.");
+ *intro_msg = true;
+ }
+ println!("WARNING: rendering difference in `{}`", concise_str(text));
+ println!(" --> {}:{}:{}", span.filename, span.loline, span.locol);
+ println!("{}", msg);
+}
+
+fn render_difference(diff: &html_diff::Difference, intro_msg: &mut bool, span: &Span, text: &str) {
match *diff {
html_diff::Difference::NodeType { ref elem, ref opposite_elem } => {
- println!(" {} Types differ: expected: `{}`, found: `{}`",
- elem.path, elem.element_name, opposite_elem.element_name);
+ print_message(&format!(" {} Types differ: expected: `{}`, found: `{}`",
+ elem.path, elem.element_name, opposite_elem.element_name),
+ intro_msg, span, text);
}
html_diff::Difference::NodeName { ref elem, ref opposite_elem } => {
- println!(" {} Tags differ: expected: `{}`, found: `{}`",
- elem.path, elem.element_name, opposite_elem.element_name);
+ print_message(&format!(" {} Tags differ: expected: `{}`, found: `{}`",
+ elem.path, elem.element_name, opposite_elem.element_name),
+ intro_msg, span, text);
}
html_diff::Difference::NodeAttributes { ref elem,
- ref elem_attributes,
- ref opposite_elem_attributes,
- .. } => {
- println!(" {} Attributes differ in `{}`: expected: `{:?}`, found: `{:?}`",
- elem.path, elem.element_name, elem_attributes, opposite_elem_attributes);
+ ref elem_attributes,
+ ref opposite_elem_attributes,
+ .. } => {
+ print_message(&format!(" {} Attributes differ in `{}`: expected: `{:?}`, \
+ found: `{:?}`",
+ elem.path, elem.element_name, elem_attributes,
+ opposite_elem_attributes),
+ intro_msg, span, text);
}
html_diff::Difference::NodeText { ref elem, ref elem_text, ref opposite_elem_text, .. } => {
if elem_text.split("\n")
.zip(opposite_elem_text.split("\n"))
.any(|(a, b)| a.trim() != b.trim()) {
let (s1, s2) = concise_compared_strs(elem_text, opposite_elem_text);
- println!(" {} Text differs:\n expected: `{}`\n found: `{}`",
- elem.path, s1, s2);
+ print_message(&format!(" {} Text differs:\n expected: `{}`\n \
+ found: `{}`",
+ elem.path, s1, s2),
+ intro_msg, span, text);
}
}
html_diff::Difference::NotPresent { ref elem, ref opposite_elem } => {
if let Some(ref elem) = *elem {
- println!(" {} One element is missing: expected: `{}`",
- elem.path, elem.element_name);
+ print_message(&format!(" {} One element is missing: expected: `{}`",
+ elem.path, elem.element_name),
+ intro_msg, span, text);
} else if let Some(ref elem) = *opposite_elem {
if elem.element_name.is_empty() {
- println!(" {} Unexpected element: `{}`",
- elem.path, concise_str(&elem.element_content));
+ print_message(&format!(" {} One element is missing: expected: `{}`",
+ elem.path, concise_str(&elem.element_content)),
+ intro_msg, span, text);
} else {
- println!(" {} Unexpected element `{}`: found: `{}`",
- elem.path, elem.element_name, concise_str(&elem.element_content));
+ print_message(&format!(" {} Unexpected element `{}`: found: `{}`",
+ elem.path, elem.element_name,
+ concise_str(&elem.element_content)),
+ intro_msg, span, text);
}
}
}
href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor)
}
};
- let mut head_len = format!("{}{}{:#}fn {}{:#}",
+ let mut head_len = format!("{}{}{}{:#}fn {}{:#}",
+ VisSpace(&meth.visibility),
ConstnessSpace(constness),
UnsafetySpace(unsafety),
AbiSpace(abi),
} else {
(0, true)
};
- write!(w, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
+ write!(w, "{}{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
{generics}{decl}{where_clause}",
+ VisSpace(&meth.visibility),
ConstnessSpace(constness),
UnsafetySpace(unsafety),
AbiSpace(abi),
render_type);
{
- let map = hir::map::map_crate(&mut hir_forest, &defs);
+ let map = hir::map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
let krate = map.krate();
let mut hir_collector = HirCollector {
sess: &sess,
check!(fs::remove_file(&filename));
}
+ #[test]
+ #[cfg(unix)]
+ fn set_get_unix_permissions() {
+ use os::unix::fs::PermissionsExt;
+
+ let tmpdir = tmpdir();
+ let filename = &tmpdir.join("set_get_unix_permissions");
+ check!(fs::create_dir(filename));
+ let mask = 0o7777;
+
+ check!(fs::set_permissions(filename,
+ fs::Permissions::from_mode(0)));
+ let metadata0 = check!(fs::metadata(filename));
+ assert_eq!(mask & metadata0.permissions().mode(), 0);
+
+ check!(fs::set_permissions(filename,
+ fs::Permissions::from_mode(0o1777)));
+ let metadata1 = check!(fs::metadata(filename));
+ assert_eq!(mask & metadata1.permissions().mode(), 0o1777);
+ }
+
#[test]
#[cfg(windows)]
fn file_test_io_seek_read_write() {
/// A reader which is always at EOF.
///
-/// This struct is generally created by calling [`empty`][empty]. Please see
-/// the documentation of `empty()` for more details.
+/// This struct is generally created by calling [`empty`]. Please see
+/// the documentation of [`empty()`][`empty`] for more details.
///
-/// [empty]: fn.empty.html
+/// [`empty`]: fn.empty.html
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Empty { _priv: () }
/// Constructs a new handle to an empty reader.
///
-/// All reads from the returned reader will return `Ok(0)`.
+/// All reads from the returned reader will return [`Ok`]`(0)`.
+///
+/// [`Ok`]: ../result/enum.Result.html#variant.Ok
///
/// # Examples
///
//
/// The pointer-sized signed integer type.
///
+/// The size of this primitive is how many bytes it takes to reference any
+/// location in memory. For example, on a 32 bit target, this is 4 bytes
+/// and on a 64 bit target, this is 8 bytes.
+///
/// *[See also the `std::isize` module](isize/index.html).*
///
/// However, please note that examples are shared between primitive integer
//
/// The pointer-sized unsigned integer type.
///
+/// The size of this primitive is how many bytes it takes to reference any
+/// location in memory. For example, on a 32 bit target, this is 4 bytes
+/// and on a 64 bit target, this is 8 bytes.
+///
/// *[See also the `std::usize` module](usize/index.html).*
///
/// However, please note that examples are shared between primitive integer
pub type socklen_t = u32;
pub type sa_family_t = u16;
-pub const AF_INET: sa_family_t = 1;
-pub const AF_INET6: sa_family_t = 2;
+pub const AF_INET: sa_family_t = 2;
+pub const AF_INET6: sa_family_t = 23;
#[derive(Copy, Clone)]
#[repr(C)]
// except according to those terms.
use super::arch::*;
-use super::data::{Stat, StatVfs, TimeSpec};
+use super::data::{SigAction, Stat, StatVfs, TimeSpec};
use super::error::Result;
use super::number::*;
-use core::mem;
+use core::{mem, ptr};
+
+// Signal restorer
+extern "C" fn restorer() -> ! {
+ sigreturn().unwrap();
+ unreachable!();
+}
/// Set the end of the process's heap
///
/// * `EIO` - an I/O error occurred
/// * `ENOENT` - `path` does not exit
/// * `ENOTDIR` - `path` is not a directory
-pub fn chdir(path: &str) -> Result<usize> {
- unsafe { syscall2(SYS_CHDIR, path.as_ptr() as usize, path.len()) }
+pub fn chdir<T: AsRef<[u8]>>(path: T) -> Result<usize> {
+ unsafe { syscall2(SYS_CHDIR, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
}
-pub fn chmod(path: &str, mode: usize) -> Result<usize> {
- unsafe { syscall3(SYS_CHMOD, path.as_ptr() as usize, path.len(), mode) }
+pub fn chmod<T: AsRef<[u8]>>(path: T, mode: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_CHMOD, path.as_ref().as_ptr() as usize, path.as_ref().len(), mode) }
}
/// Produce a fork of the current process, or a new process thread
unsafe { syscall2(SYS_FTRUNCATE, fd, len) }
}
+// Change modify and/or access times
+pub fn futimens(fd: usize, times: &[TimeSpec]) -> Result<usize> {
+ unsafe { syscall3(SYS_FUTIMENS, fd, times.as_ptr() as usize,
+ times.len() * mem::size_of::<TimeSpec>()) }
+}
+
/// Fast userspace mutex
pub unsafe fn futex(addr: *mut i32, op: usize, val: i32, val2: usize, addr2: *mut i32)
-> Result<usize> {
unsafe { syscall0(SYS_GETPID) }
}
+/// Get the process group ID
+pub fn getpgid(pid: usize) -> Result<usize> {
+ unsafe { syscall1(SYS_GETPGID, pid) }
+}
+
+/// Get the parent process ID
+pub fn getppid() -> Result<usize> {
+ unsafe { syscall0(SYS_GETPPID) }
+}
+
/// Get the current user ID
pub fn getuid() -> Result<usize> {
unsafe { syscall0(SYS_GETUID) }
}
/// Open a file
-pub fn open(path: &str, flags: usize) -> Result<usize> {
- unsafe { syscall3(SYS_OPEN, path.as_ptr() as usize, path.len(), flags) }
+pub fn open<T: AsRef<[u8]>>(path: T, flags: usize) -> Result<usize> {
+ unsafe { syscall3(SYS_OPEN, path.as_ref().as_ptr() as usize, path.as_ref().len(), flags) }
}
/// Allocate pages, linearly in physical memory
}
/// Remove a directory
-pub fn rmdir(path: &str) -> Result<usize> {
- unsafe { syscall2(SYS_RMDIR, path.as_ptr() as usize, path.len()) }
+pub fn rmdir<T: AsRef<[u8]>>(path: T) -> Result<usize> {
+ unsafe { syscall2(SYS_RMDIR, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
+}
+
+/// Set the process group ID
+pub fn setpgid(pid: usize, pgid: usize) -> Result<usize> {
+ unsafe { syscall2(SYS_SETPGID, pid, pgid) }
}
/// Set the current process group IDs
unsafe { syscall2(SYS_SETREUID, ruid, euid) }
}
+/// Set up a signal handler
+pub fn sigaction(sig: usize, act: Option<&SigAction>, oldact: Option<&mut SigAction>)
+-> Result<usize> {
+ unsafe { syscall4(SYS_SIGACTION, sig,
+ act.map(|x| x as *const _).unwrap_or_else(ptr::null) as usize,
+ oldact.map(|x| x as *mut _).unwrap_or_else(ptr::null_mut) as usize,
+ restorer as usize) }
+}
+
+// Return from signal handler
+pub fn sigreturn() -> Result<usize> {
+ unsafe { syscall0(SYS_SIGRETURN) }
+}
+
/// Remove a file
-pub fn unlink(path: &str) -> Result<usize> {
- unsafe { syscall2(SYS_UNLINK, path.as_ptr() as usize, path.len()) }
+pub fn unlink<T: AsRef<[u8]>>(path: T) -> Result<usize> {
+ unsafe { syscall2(SYS_UNLINK, path.as_ref().as_ptr() as usize, path.as_ref().len()) }
}
/// Convert a virtual address to a physical one
use core::ops::{Deref, DerefMut};
use core::{mem, slice};
+#[derive(Copy, Clone, Debug, Default)]
+pub struct Event {
+ pub id: usize,
+ pub flags: usize,
+ pub data: usize
+}
+
+impl Deref for Event {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const Event as *const u8,
+ mem::size_of::<Event>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for Event {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut Event as *mut u8,
+ mem::size_of::<Event>()
+ ) as &mut [u8]
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C)]
+pub struct Packet {
+ pub id: u64,
+ pub pid: usize,
+ pub uid: u32,
+ pub gid: u32,
+ pub a: usize,
+ pub b: usize,
+ pub c: usize,
+ pub d: usize
+}
+
+impl Deref for Packet {
+ type Target = [u8];
+ fn deref(&self) -> &[u8] {
+ unsafe {
+ slice::from_raw_parts(
+ self as *const Packet as *const u8,
+ mem::size_of::<Packet>()
+ ) as &[u8]
+ }
+ }
+}
+
+impl DerefMut for Packet {
+ fn deref_mut(&mut self) -> &mut [u8] {
+ unsafe {
+ slice::from_raw_parts_mut(
+ self as *mut Packet as *mut u8,
+ mem::size_of::<Packet>()
+ ) as &mut [u8]
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct SigAction {
+ pub sa_handler: extern "C" fn(usize),
+ pub sa_mask: [u64; 2],
+ pub sa_flags: usize,
+}
+
+impl Default for SigAction {
+ fn default() -> Self {
+ Self {
+ sa_handler: unsafe { mem::transmute(0usize) },
+ sa_mask: [0; 2],
+ sa_flags: 0,
+ }
+ }
+}
+
#[derive(Copy, Clone, Debug, Default)]
#[repr(C)]
pub struct Stat {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
- slice::from_raw_parts(self as *const Stat as *const u8,
- mem::size_of::<Stat>()) as &[u8]
+ slice::from_raw_parts(
+ self as *const Stat as *const u8,
+ mem::size_of::<Stat>()
+ ) as &[u8]
}
}
}
impl DerefMut for Stat {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
- slice::from_raw_parts_mut(self as *mut Stat as *mut u8,
- mem::size_of::<Stat>()) as &mut [u8]
+ slice::from_raw_parts_mut(
+ self as *mut Stat as *mut u8,
+ mem::size_of::<Stat>()
+ ) as &mut [u8]
}
}
}
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
- slice::from_raw_parts(self as *const StatVfs as *const u8,
- mem::size_of::<StatVfs>()) as &[u8]
+ slice::from_raw_parts(
+ self as *const StatVfs as *const u8,
+ mem::size_of::<StatVfs>()
+ ) as &[u8]
}
}
}
impl DerefMut for StatVfs {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
- slice::from_raw_parts_mut(self as *mut StatVfs as *mut u8,
- mem::size_of::<StatVfs>()) as &mut [u8]
+ slice::from_raw_parts_mut(
+ self as *mut StatVfs as *mut u8,
+ mem::size_of::<StatVfs>()
+ ) as &mut [u8]
}
}
}
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
- slice::from_raw_parts(self as *const TimeSpec as *const u8,
- mem::size_of::<TimeSpec>()) as &[u8]
+ slice::from_raw_parts(
+ self as *const TimeSpec as *const u8,
+ mem::size_of::<TimeSpec>()
+ ) as &[u8]
}
}
}
impl DerefMut for TimeSpec {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
- slice::from_raw_parts_mut(self as *mut TimeSpec as *mut u8,
- mem::size_of::<TimeSpec>()) as &mut [u8]
+ slice::from_raw_parts_mut(
+ self as *mut TimeSpec as *mut u8,
+ mem::size_of::<TimeSpec>()
+ ) as &mut [u8]
}
}
}
pub const CLONE_VM: usize = 0x100;
pub const CLONE_FS: usize = 0x200;
pub const CLONE_FILES: usize = 0x400;
+pub const CLONE_SIGHAND: usize = 0x800;
pub const CLONE_VFORK: usize = 0x4000;
+pub const CLONE_THREAD: usize = 0x10000;
pub const CLOCK_REALTIME: usize = 1;
pub const CLOCK_MONOTONIC: usize = 4;
pub const EVENT_READ: usize = 1;
pub const EVENT_WRITE: usize = 2;
+pub const F_DUPFD: usize = 0;
pub const F_GETFD: usize = 1;
pub const F_SETFD: usize = 2;
pub const F_GETFL: usize = 3;
pub const MODE_DIR: u16 = 0x4000;
pub const MODE_FILE: u16 = 0x8000;
pub const MODE_SYMLINK: u16 = 0xA000;
+pub const MODE_FIFO: u16 = 0x1000;
+pub const MODE_CHR: u16 = 0x2000;
pub const MODE_PERM: u16 = 0x0FFF;
pub const MODE_SETUID: u16 = 0o4000;
pub const SIGPWR: usize = 30;
pub const SIGSYS: usize = 31;
+pub const SIG_DFL: usize = 0;
+pub const SIG_IGN: usize = 1;
+
+pub const SA_NOCLDSTOP: usize = 0x00000001;
+pub const SA_NOCLDWAIT: usize = 0x00000002;
+pub const SA_SIGINFO: usize = 0x00000004;
+pub const SA_RESTORER: usize = 0x04000000;
+pub const SA_ONSTACK: usize = 0x08000000;
+pub const SA_RESTART: usize = 0x10000000;
+pub const SA_NODEFER: usize = 0x40000000;
+pub const SA_RESETHAND: usize = 0x80000000;
+
pub const WNOHANG: usize = 1;
pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100;
pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118;
pub const SYS_FTRUNCATE: usize =SYS_CLASS_FILE | 93;
+pub const SYS_FUTIMENS: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 320;
pub const SYS_BRK: usize = 45;
pub const SYS_CHDIR: usize = 12;
pub const SYS_GETGID: usize = 200;
pub const SYS_GETNS: usize = 950;
pub const SYS_GETPID: usize = 20;
+pub const SYS_GETPGID: usize = 132;
+pub const SYS_GETPPID: usize = 64;
pub const SYS_GETUID: usize = 199;
pub const SYS_IOPL: usize = 110;
pub const SYS_KILL: usize = 37;
pub const SYS_PHYSUNMAP: usize =948;
pub const SYS_VIRTTOPHYS: usize=949;
pub const SYS_PIPE2: usize = 331;
+pub const SYS_SETPGID: usize = 57;
pub const SYS_SETREGID: usize = 204;
pub const SYS_SETRENS: usize = 952;
pub const SYS_SETREUID: usize = 203;
+pub const SYS_SIGACTION: usize =67;
+pub const SYS_SIGRETURN: usize =119;
pub const SYS_WAITPID: usize = 7;
pub const SYS_YIELD: usize = 158;
/// Unix-specific extensions to `Permissions`
#[stable(feature = "fs_ext", since = "1.1.0")]
pub trait PermissionsExt {
- /// Returns the underlying raw `mode_t` bits that are the standard Unix
- /// permissions for this file.
+ /// Returns the underlying raw `st_mode` bits that contain the standard
+ /// Unix permissions for this file.
///
/// # Examples
///
impl FileAttr {
pub fn size(&self) -> u64 { self.stat.st_size as u64 }
pub fn perm(&self) -> FilePermissions {
- FilePermissions { mode: (self.stat.st_mode as mode_t) & 0o777 }
+ FilePermissions { mode: (self.stat.st_mode as mode_t) }
}
pub fn file_type(&self) -> FileType {
let ret;
let fileline_count = {
let state = unsafe { init_state() };
+ if state.is_null() {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "failed to allocate libbacktrace state")
+ )
+ }
let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
ret = unsafe {
let symname = {
let state = unsafe { init_state() };
if state.is_null() {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "failed to allocate libbacktrace state")
+ )
+ }
+ let mut data = ptr::null();
+ let data_addr = &mut data as *mut *const libc::c_char;
+ let ret = unsafe {
+ backtrace_syminfo(state,
+ frame.symbol_addr as libc::uintptr_t,
+ syminfo_cb,
+ error_cb,
+ data_addr as *mut libc::c_void)
+ };
+ if ret == 0 || data.is_null() {
None
} else {
- let mut data = ptr::null();
- let data_addr = &mut data as *mut *const libc::c_char;
- let ret = unsafe {
- backtrace_syminfo(state,
- frame.symbol_addr as libc::uintptr_t,
- syminfo_cb,
- error_cb,
- data_addr as *mut libc::c_void)
- };
- if ret == 0 || data.is_null() {
- None
- } else {
- unsafe {
- CStr::from_ptr(data).to_str().ok()
- }
+ unsafe {
+ CStr::from_ptr(data).to_str().ok()
}
}
};
#[unstable(feature = "decode_utf8", issue = "33906")]
pub use core::char::{DecodeUtf8, decode_utf8};
#[unstable(feature = "unicode", issue = "27783")]
-pub use tables::UNICODE_VERSION;
+pub use tables::{UnicodeVersion, UNICODE_VERSION};
/// Returns an iterator that yields the lowercase equivalent of a `char`.
///
.and_then(|at| at.value_str())
}
+/// Check if `attrs` contains an attribute like `#![feature(feature_name)]`.
+/// This will not perform any "sanity checks" on the form of the attributes.
+pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
+ attrs.iter().any(|item| {
+ item.check_name("feature") &&
+ item.meta_item_list().map(|list| {
+ list.iter().any(|mi| {
+ mi.word().map(|w| w.name() == feature_name)
+ .unwrap_or(false)
+ })
+ }).unwrap_or(false)
+ })
+}
+
/* Higher-level applications */
pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
// allow '|' at beginning of match arms (RFC 1925)
(active, match_beginning_vert, "1.21.0", Some(44101)),
+
+ // Copy/Clone closures (RFC 2132)
+ (active, clone_closures, "1.22.0", Some(44490)),
+ (active, copy_closures, "1.22.0", Some(44490)),
+
+ // allow `'_` placeholder lifetimes
+ (active, underscore_lifetimes, "1.22.0", Some(44524)),
);
declare_features! (
}
visit::walk_lifetime_def(self, lifetime_def)
}
+
+ fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) {
+ if lt.ident.name == "'_" {
+ gate_feature_post!(&self, underscore_lifetimes, lt.span,
+ "underscore lifetimes are unstable");
+ }
+ visit::walk_lifetime(self, lt)
+ }
}
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {
let mut features = Features::new();
- let mut feature_checker = MutexFeatureChecker::default();
+ let mut feature_checker = FeatureChecker::default();
for attr in krate_attrs {
if !attr.check_name("feature") {
features
}
-// A collector for mutually-exclusive features and their flag spans
+/// A collector for mutually exclusive and interdependent features and their flag spans.
#[derive(Default)]
-struct MutexFeatureChecker {
+struct FeatureChecker {
proc_macro: Option<Span>,
custom_attribute: Option<Span>,
+ copy_closures: Option<Span>,
+ clone_closures: Option<Span>,
}
-impl MutexFeatureChecker {
+impl FeatureChecker {
// If this method turns out to be a hotspot due to branching,
// the branching can be eliminated by modifying `set!()` to set these spans
// only for the features that need to be checked for mutual exclusion.
if features.custom_attribute {
self.custom_attribute = self.custom_attribute.or(Some(span));
}
+
+ if features.copy_closures {
+ self.copy_closures = self.copy_closures.or(Some(span));
+ }
+
+ if features.clone_closures {
+ self.clone_closures = self.clone_closures.or(Some(span));
+ }
}
fn check(self, handler: &Handler) {
panic!(FatalError);
}
+
+ if let (Some(span), None) = (self.copy_closures, self.clone_closures) {
+ handler.struct_span_err(span, "`#![feature(copy_closures)]` can only be used with \
+ `#![feature(clone_closures)]`")
+ .span_note(span, "`#![feature(copy_closures)]` declared here")
+ .emit();
+
+ panic!(FatalError);
+ }
}
}
extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM) {
const TargetMachine *Target = unwrap(TM);
const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
+ const Triple::ArchType HostArch = Triple(sys::getProcessTriple()).getArch();
+ const Triple::ArchType TargetArch = Target->getTargetTriple().getArch();
const ArrayRef<SubtargetFeatureKV> CPUTable = MCInfo->getCPUTable();
unsigned MaxCPULen = getLongestEntryLength(CPUTable);
printf("Available CPUs for this target:\n");
+ if (HostArch == TargetArch) {
+ const StringRef HostCPU = sys::getHostCPUName();
+ printf(" %-*s - Select the CPU of the current host (currently %.*s).\n",
+ MaxCPULen, "native", (int)HostCPU.size(), HostCPU.data());
+ }
for (auto &CPU : CPUTable)
printf(" %-*s - %s.\n", MaxCPULen, CPU.Key, CPU.Desc);
printf("\n");
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+#![feature(underscore_lifetimes)]
+
+struct Foo<'a: '_>(&'a u8); //~ ERROR invalid lifetime bound name: `'_`
+fn foo<'a: '_>(_: &'a u8) {} //~ ERROR invalid lifetime bound name: `'_`
+
+struct Bar<'a>(&'a u8);
+impl<'a: '_> Bar<'a> { //~ ERROR invalid lifetime bound name: `'_`
+ fn bar() {}
+}
+
+fn main() {}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[derive(Clone)]
+struct S(i32);
+
+fn main() {
+ let a = S(5);
+ let hello = move || {
+ println!("Hello {}", a.0);
+ };
+
+ let hello = hello.clone(); //~ ERROR no method named `clone` found for type
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ let a = 5;
+ let hello = || {
+ println!("Hello {}", a);
+ };
+
+ let b = hello;
+ let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: --cap-lints allow
+
+// This tests that the fn_must_use feature-gate warning respects the lint
+// cap. (See discussion in Issue #44213.)
+
+#![feature(rustc_attrs)]
+
+#[must_use] // (no feature-gate warning because of the lint cap!)
+fn need_to_use_it() -> bool { true }
+
+#[rustc_error]
+fn main() {} //~ ERROR compilation successful
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+struct Foo<'a>(&'a u8);
+
+fn foo(x: &u8) -> Foo<'_> { //~ ERROR underscore lifetimes are unstable
+ Foo(x)
+}
+
+fn main() {
+ let x = 5;
+ let _ = foo(&x);
+}
--- /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.
+
+fn main() {
+ '_: loop { //~ ERROR invalid label name `'_`
+ break '_ //~ ERROR invalid label name `'_`
+ }
+}
+++ /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.
-
-fn _f<'_>() //~ ERROR invalid lifetime name `'_`
- -> &'_ u8 //~ ERROR invalid lifetime name `'_`
-{
- panic!();
-}
-
-fn main() {
- '_: loop { //~ ERROR invalid label name `'_`
- break '_ //~ ERROR invalid label name `'_`
- }
-}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that closures do not implement `Clone` if their environment is not `Clone`.
+
+#![feature(clone_closures)]
+
+struct S(i32);
+
+fn main() {
+ let a = S(5);
+ let hello = move || {
+ println!("Hello {}", a.0);
+ };
+
+ let hello = hello.clone(); //~ ERROR the trait bound `S: std::clone::Clone` is not satisfied
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that closures do not implement `Copy` if their environment is not `Copy`.
+
+#![feature(copy_closures)]
+#![feature(clone_closures)]
+
+fn main() {
+ let mut a = 5;
+ let hello = || {
+ a += 1;
+ };
+
+ let b = hello;
+ let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(underscore_lifetimes)]
+
+struct Foo<'a>(&'a u8);
+struct Baz<'a>(&'_ &'a u8); //~ ERROR missing lifetime specifier
+
+impl Foo<'_> { //~ ERROR missing lifetime specifier
+ fn x() {}
+}
+
+fn foo<'_> //~ ERROR invalid lifetime parameter name: `'_`
+(_: Foo<'_>) {}
+
+trait Meh<'a> {}
+impl<'a> Meh<'a> for u8 {}
+
+fn meh() -> Box<for<'_> Meh<'_>> //~ ERROR invalid lifetime parameter name: `'_`
+//~^ ERROR missing lifetime specifier
+//~^^ ERROR missing lifetime specifier
+{
+ Box::new(5u8)
+}
+
+fn foo2(_: &'_ u8, y: &'_ u8) -> &'_ u8 { y } //~ ERROR missing lifetime specifier
+
+fn main() {
+ let x = 5;
+ foo(Foo(&x));
+ let _ = meh();
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(underscore_lifetimes)]
+
+fn foo(x: &mut Vec<&'_ u8>, y: &'_ u8) { x.push(y); } //~ ERROR lifetime mismatch
+
+fn main() {}
--- /dev/null
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <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(unicode)]
+
+
+/// Tests access to the internal Unicode Version type and value.
+pub fn main() {
+ check(std::char::UNICODE_VERSION);
+}
+
+pub fn check(unicode_version: std::char::UnicodeVersion) {
+ assert!(unicode_version.major >= 10);
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that closures implement `Clone`.
+
+#![feature(clone_closures)]
+
+#[derive(Clone)]
+struct S(i32);
+
+fn main() {
+ let mut a = S(5);
+ let mut hello = move || {
+ a.0 += 1;
+ println!("Hello {}", a.0);
+ a.0
+ };
+
+ let mut hello2 = hello.clone();
+ assert_eq!(6, hello2());
+ assert_eq!(6, hello());
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Check that closures implement `Copy`.
+
+#![feature(copy_closures)]
+#![feature(clone_closures)]
+
+fn call<T, F: FnOnce() -> T>(f: F) -> T { f() }
+
+fn main() {
+ let a = 5;
+ let hello = || {
+ println!("Hello {}", a);
+ a
+ };
+
+ assert_eq!(5, call(hello.clone()));
+ assert_eq!(5, call(hello));
+ assert_eq!(5, call(hello));
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+fn main() {
+ let _a = || {
+ yield;
+ let a = String::new();
+ a.len()
+ };
+}
\ No newline at end of file
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that a borrow that occurs after a yield in the same
+// argument list is not treated as live across the yield by
+// type-checking.
+
+#![feature(generators)]
+
+fn foo(_a: (), _b: &bool) {}
+
+fn bar() {
+ || {
+ let b = true;
+ foo(yield, &b);
+ };
+}
+
+fn main() { }
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that box-statements with yields in them work.
+
+#![feature(generators, box_syntax)]
+
+fn main() {
+ let x = 0i32;
+ || {
+ let y = 2u32;
+ {
+ let _t = box (&x, yield 0, &y);
+ }
+ match box (&x, yield 0, &y) {
+ _t => {}
+ }
+ };
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(underscore_lifetimes)]
+
+struct Foo<'a>(&'a u8);
+
+fn foo(x: &u8) -> Foo<'_> {
+ Foo(x)
+}
+
+fn foo2(x: &'_ u8) -> Foo<'_> {
+ Foo(x)
+}
+
+fn foo3(x: &'_ u8) -> Foo {
+ Foo(x)
+}
+
+fn foo4(_: Foo<'_>) {}
+
+struct Foo2<'a, 'b> {
+ a: &'a u8,
+ b: &'b u8,
+}
+fn foo5<'b>(foo: Foo2<'_, 'b>) -> &'b u8 {
+ foo.b
+}
+
+fn main() {
+ let x = &5;
+ let _ = foo(x);
+ let _ = foo2(x);
+ let _ = foo3(x);
+ foo4(Foo(x));
+ let _ = foo5(Foo2 {
+ a: x,
+ b: &6,
+ });
+}
--- /dev/null
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+// compile-flags: --no-defaults --passes collapse-docs --passes unindent-comments --passes strip-priv-imports
+
+#![crate_name = "foo"]
+
+// @has foo/fn.bar.html
+// @has - '//*[@class="rust fn"]' 'pub fn bar() -> '
+/// foo
+pub fn bar() -> usize {
+ 2
+}
+
+// @has foo/struct.Foo.html
+// @has - '//*[@class="method"]' 'pub fn new()'
+// @has - '//*[@class="method"]' 'fn not_pub()'
+pub struct Foo(usize);
+
+impl Foo {
+ pub fn new() -> Foo { Foo(0) }
+ fn not_pub() {}
+}
= note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:26:17: 30:6 a:&std::cell::Cell<i32> _]`
= note: required by `main::assert_send`
-error[E0277]: the trait bound `std::cell::Cell<i32>: std::marker::Sync` is not satisfied in `[generator@$DIR/not-send-sync.rs:19:17: 23:6 ((), std::cell::Cell<i32>)]`
+error[E0277]: the trait bound `std::cell::Cell<i32>: std::marker::Sync` is not satisfied in `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell<i32>, ())]`
--> $DIR/not-send-sync.rs:19:5
|
19 | assert_sync(|| {
| ^^^^^^^^^^^ `std::cell::Cell<i32>` cannot be shared between threads safely
|
- = help: within `[generator@$DIR/not-send-sync.rs:19:17: 23:6 ((), std::cell::Cell<i32>)]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<i32>`
- = note: required because it appears within the type `((), std::cell::Cell<i32>)`
- = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:19:17: 23:6 ((), std::cell::Cell<i32>)]`
+ = help: within `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell<i32>, ())]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<i32>`
+ = note: required because it appears within the type `(std::cell::Cell<i32>, ())`
+ = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:19:17: 23:6 (std::cell::Cell<i32>, ())]`
= note: required by `main::assert_sync`
error: aborting due to 2 previous errors
+++ /dev/null
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-#![feature(generators)]
-
-fn foo(_a: (), _b: &bool) {}
-
-// Some examples that probably *could* be accepted, but which we reject for now.
-
-fn bar() {
- || {
- let b = true;
- foo(yield, &b); //~ ERROR
- };
-}
-
-fn main() { }
+++ /dev/null
-error[E0626]: borrow may still be in use when generator yields
- --> $DIR/yield-in-args-rev.rs:20:21
- |
-20 | foo(yield, &b); //~ ERROR
- | ----- ^
- | |
- | possible yield occurs here
-
-error: aborting due to previous error
-
|
help: possible better candidate is found in another module, you can import it into scope
|
-16 | use m::n::Z;
+22 | use m::n::Z;
|
error[E0423]: expected value, found struct `S`
|
help: possible better candidate is found in another module, you can import it into scope
|
-15 | use m::S;
+32 | use m::S;
|
error[E0423]: expected value, found struct `xcrate::S`
|
help: possible better candidate is found in another module, you can import it into scope
|
-15 | use m::S;
+32 | use m::S;
|
error[E0603]: tuple struct `Z` is private
pub const A: i32 = 0;
}
+mod foo {
+ #[derive(Debug)]
+ pub struct Foo;
+
+ // test whether the use suggestion isn't
+ // placed into the expansion of `#[derive(Debug)]
+ type Bar = Path;
+}
+
fn main() {
y!();
let _ = A;
+error[E0412]: cannot find type `Path` in this scope
+ --> $DIR/use_suggestion_placement.rs:25:16
+ |
+25 | type Bar = Path;
+ | ^^^^ not found in this scope
+ |
+help: possible candidate is found in another module, you can import it into scope
+ |
+21 | use std::path::Path;
+ |
+
error[E0425]: cannot find value `A` in this scope
- --> $DIR/use_suggestion_placement.rs:21:13
+ --> $DIR/use_suggestion_placement.rs:30:13
|
-21 | let _ = A;
+30 | let _ = A;
| ^ not found in this scope
|
help: possible candidate is found in another module, you can import it into scope
|
error[E0412]: cannot find type `HashMap` in this scope
- --> $DIR/use_suggestion_placement.rs:26:23
+ --> $DIR/use_suggestion_placement.rs:35:23
|
-26 | type Dict<K, V> = HashMap<K, V>;
+35 | type Dict<K, V> = HashMap<K, V>;
| ^^^^^^^ not found in this scope
|
help: possible candidates are found in other modules, you can import them into scope
|
error[E0091]: type parameter `K` is unused
- --> $DIR/use_suggestion_placement.rs:26:15
+ --> $DIR/use_suggestion_placement.rs:35:15
|
-26 | type Dict<K, V> = HashMap<K, V>;
+35 | type Dict<K, V> = HashMap<K, V>;
| ^ unused type parameter
error[E0091]: type parameter `V` is unused
- --> $DIR/use_suggestion_placement.rs:26:18
+ --> $DIR/use_suggestion_placement.rs:35:18
|
-26 | type Dict<K, V> = HashMap<K, V>;
+35 | type Dict<K, V> = HashMap<K, V>;
| ^ unused type parameter
-error: aborting due to 4 previous errors
+error: aborting due to 5 previous errors
|
help: possible better candidate is found in another module, you can import it into scope
|
-11 | use std::ops::Add;
+13 | use std::ops::Add;
|
error[E0601]: main function not found
cmd.arg("--no-tty")
.arg("--yes")
.arg("--passphrase-fd").arg("0")
+ .arg("--personal-digest-preferences").arg("SHA512")
.arg("--armor")
.arg("--output").arg(&asc)
.arg("--detach-sign").arg(path)
# configures whether the tool is included in the Rust distribution.
#
# If a tool was working before your PR but is broken now, consider
-# updating the tool within your PR. How to do that is described in
+# opening a PR against the tool so that it works with your changes.
+# If the tool stops compiling, change its state to `Broken`. If it
+# still builds, change it to `Compiling`.
+# How to do that is described in
# "CONTRIBUTING.md#External Dependencies". If the effort required is not
# warranted (e.g. due to the tool abusing some API that you changed, and
-# fixing the tool would mean a significant refactoring), you can disable
-# the tool here, by changing its state to `Broken`. Remember to ping
-# the tool authors if you do not fix their tool, so they can proactively
-# fix it, instead of being surprised by the breakage.
+# fixing the tool would mean a significant refactoring) remember to ping
+# the tool authors, so they can fix it, instead of being surprised by the
+# breakage.
#
# Each tool has a list of people to ping
# ping @oli-obk @RalfJung @eddyb
miri = "Testing"
+
+# ping @Manishearth @llogiq @mcarton @oli-obk
+clippy = "Broken"