diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index 69355c6c6cc0b..0b800cacfc19a 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -113,10 +113,23 @@ pub trait Clone : Sized { } } -// FIXME(aburka): this method is used solely by #[derive] to -// assert that every component of a type implements Clone. +// FIXME(aburka): these structs are used solely by #[derive] to +// assert that every component of a type implements Clone or Copy. // -// This should never be called by user code. +// These structs should never appear in user code. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_clone_copy", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsClone { _field: ::marker::PhantomData } +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_clone_copy", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsCopy { _field: ::marker::PhantomData } +#[cfg(stage0)] #[doc(hidden)] #[inline(always)] #[unstable(feature = "derive_clone_copy", diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 670978a2d49af..f990a27e52b31 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -129,7 +129,7 @@ pub trait PartialEq { /// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has /// no extra methods, it is only informing the compiler that this is an /// equivalence relation rather than a partial equivalence relation. Note that -/// the `derive` strategy requires all fields are `PartialEq`, which isn't +/// the `derive` strategy requires all fields are `Eq`, which isn't /// always desired. /// /// ## How can I implement `Eq`? @@ -165,6 +165,17 @@ pub trait Eq: PartialEq { fn assert_receiver_is_total_eq(&self) {} } +// FIXME: this struct is used solely by #[derive] to +// assert that every component of a type implements Eq. +// +// This struct should never appear in user code. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_eq", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsEq { _field: ::marker::PhantomData } + /// An `Ordering` is the result of a comparison between two values. /// /// # Examples diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 7b147faccd20f..d1df56905df24 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -520,8 +520,8 @@ impl ops::Index for [T] { type Output = T; fn index(&self, index: usize) -> &T { - assert!(index < self.len()); - unsafe { self.get_unchecked(index) } + // NB built-in indexing + &(*self)[index] } } @@ -530,8 +530,8 @@ impl ops::Index for [T] { impl ops::IndexMut for [T] { #[inline] fn index_mut(&mut self, index: usize) -> &mut T { - assert!(index < self.len()); - unsafe { self.get_unchecked_mut(index) } + // NB built-in indexing + &mut (*self)[index] } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 59431f3f02dce..39fc50666a8ce 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -830,6 +830,33 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { result.map(move |t| InferOk { value: t, obligations: fields.obligations }) } + // Clear the "obligations in snapshot" flag, invoke the closure, + // then restore the flag to its original value. This flag is a + // debugging measure designed to detect cases where we start a + // snapshot, create type variables, register obligations involving + // those type variables in the fulfillment cx, and then have to + // unroll the snapshot, leaving "dangling type variables" behind. + // In such cases, the flag will be set by the fulfillment cx, and + // an assertion will fail when rolling the snapshot back. Very + // useful, much better than grovelling through megabytes of + // RUST_LOG output. + // + // HOWEVER, in some cases the flag is wrong. In particular, we + // sometimes create a "mini-fulfilment-cx" in which we enroll + // obligations. As long as this fulfillment cx is fully drained + // before we return, this is not a problem, as there won't be any + // escaping obligations in the main cx. In those cases, you can + // use this function. + pub fn save_and_restore_obligations_in_snapshot_flag(&self, func: F) -> R + where F: FnOnce(&Self) -> R + { + let flag = self.obligations_in_snapshot.get(); + self.obligations_in_snapshot.set(false); + let result = func(self); + self.obligations_in_snapshot.set(flag); + result + } + fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index b8465e63b1c8a..b2c293a290ab3 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -39,6 +39,7 @@ use std::rc::Rc; use std::path::PathBuf; use syntax::ast; use syntax::attr; +use syntax::ext::base::LoadedMacro; use syntax::ptr::P; use syntax::parse::token::InternedString; use syntax_pos::Span; @@ -492,6 +493,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") } } +pub trait MacroLoader { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; +} /// Metadata encoding and decoding can make use of thread-local encoding and /// decoding contexts. These allow implementers of serialize::Encodable and diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3477ec6f99af1..49686d63ee43b 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -21,7 +21,7 @@ use util::nodemap::{NodeMap, FnvHashMap}; use util::common::duration_to_secs_str; use mir::transform as mir_pass; -use syntax::ast::{NodeId, Name}; +use syntax::ast::NodeId; use errors::{self, DiagnosticBuilder}; use errors::emitter::{Emitter, EmitterWriter}; use syntax::json::JsonEmitter; @@ -39,7 +39,7 @@ use llvm; use std::path::{Path, PathBuf}; use std::cell::{self, Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::env; use std::ffi::CString; use std::rc::Rc; @@ -96,10 +96,6 @@ pub struct Session { pub injected_allocator: Cell>, pub injected_panic_runtime: Cell>, - /// Names of all bang-style macros and syntax extensions - /// available in this crate - pub available_macros: RefCell>, - /// Map from imported macro spans (which consist of /// the localized span for the macro body) to the /// macro name and defintion span in the source crate. @@ -552,7 +548,6 @@ pub fn build_session_(sopts: config::Options, next_node_id: Cell::new(1), injected_allocator: Cell::new(None), injected_panic_runtime: Cell::new(None), - available_macros: RefCell::new(HashSet::new()), imported_macro_spans: RefCell::new(HashMap::new()), incr_comp_session: RefCell::new(IncrCompSession::NotInitialized), perf_stats: PerfStats { diff --git a/src/librustc/traits/specialize/mod.rs b/src/librustc/traits/specialize/mod.rs index f3ba4d16eb0b2..2f63526bf6c27 100644 --- a/src/librustc/traits/specialize/mod.rs +++ b/src/librustc/traits/specialize/mod.rs @@ -203,32 +203,34 @@ fn fulfill_implication<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) - let mut fulfill_cx = FulfillmentContext::new(); - for oblig in obligations.into_iter() { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } - match fulfill_cx.select_all_or_error(infcx) { - Err(errors) => { - // no dice! - debug!("fulfill_implication: for impls on {:?} and {:?}, could not fulfill: {:?} given \ - {:?}", - source_trait_ref, - target_trait_ref, - errors, - infcx.parameter_environment.caller_bounds); - Err(()) + infcx.save_and_restore_obligations_in_snapshot_flag(|infcx| { + let mut fulfill_cx = FulfillmentContext::new(); + for oblig in obligations.into_iter() { + fulfill_cx.register_predicate_obligation(&infcx, oblig); } + match fulfill_cx.select_all_or_error(infcx) { + Err(errors) => { + // no dice! + debug!("fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait_ref, + target_trait_ref, + errors, + infcx.parameter_environment.caller_bounds); + Err(()) + } - Ok(()) => { - debug!("fulfill_implication: an impl for {:?} specializes {:?}", - source_trait_ref, - target_trait_ref); + Ok(()) => { + debug!("fulfill_implication: an impl for {:?} specializes {:?}", + source_trait_ref, + target_trait_ref); - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_type_vars_if_possible(&target_substs)) + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_type_vars_if_possible(&target_substs)) + } } - } + }) } pub struct SpecializesCache { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 20601493d68f3..6d7a2d6cba1c7 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1303,7 +1303,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } pub fn mk_trait(self, mut obj: TraitObject<'tcx>) -> Ty<'tcx> { - obj.projection_bounds.sort_by(|a, b| a.sort_key().cmp(&b.sort_key())); + obj.projection_bounds.sort_by_key(|b| b.sort_key(self)); self.mk_ty(TyTrait(box obj)) } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index f634c8e37d7bd..14eb2fb7914c3 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1018,10 +1018,6 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { pub fn item_name(&self) -> Name { self.0.projection_ty.item_name // safe to skip the binder to access a name } - - pub fn sort_key(&self) -> (DefId, Name) { - self.0.projection_ty.sort_key() - } } pub trait ToPolyTraitRef<'tcx> { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index a755dd056cd84..5fdc7abc0af5b 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -23,7 +23,7 @@ use std::mem; use std::ops; use syntax::abi; use syntax::ast::{self, Name}; -use syntax::parse::token::keywords; +use syntax::parse::token::{keywords, InternedString}; use serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -440,12 +440,6 @@ pub struct ProjectionTy<'tcx> { pub item_name: Name, } -impl<'tcx> ProjectionTy<'tcx> { - pub fn sort_key(&self) -> (DefId, Name) { - (self.trait_ref.def_id, self.item_name) - } -} - #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct BareFnTy<'tcx> { pub unsafety: hir::Unsafety, @@ -738,8 +732,17 @@ impl<'a, 'tcx, 'gcx> PolyExistentialProjection<'tcx> { self.0.item_name // safe to skip the binder to access a name } - pub fn sort_key(&self) -> (DefId, Name) { - (self.0.trait_ref.def_id, self.0.item_name) + pub fn sort_key(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> (u64, InternedString) { + // We want something here that is stable across crate boundaries. + // The DefId isn't but the `deterministic_hash` of the corresponding + // DefPath is. + let trait_def = tcx.lookup_trait_def(self.0.trait_ref.def_id); + let def_path_hash = trait_def.def_path_hash; + + // An `ast::Name` is also not stable (it's just an index into an + // interning table), so map to the corresponding `InternedString`. + let item_name = self.0.item_name.as_str(); + (def_path_hash, item_name) } pub fn with_self_ty(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>, diff --git a/src/librustc/ty/trait_def.rs b/src/librustc/ty/trait_def.rs index 61285e8f8b0a5..268b2fcaa4adb 100644 --- a/src/librustc/ty/trait_def.rs +++ b/src/librustc/ty/trait_def.rs @@ -70,7 +70,11 @@ pub struct TraitDef<'tcx> { pub specialization_graph: RefCell, /// Various flags - pub flags: Cell + pub flags: Cell, + + /// The ICH of this trait's DefPath, cached here so it doesn't have to be + /// recomputed all the time. + pub def_path_hash: u64, } impl<'a, 'gcx, 'tcx> TraitDef<'tcx> { @@ -78,7 +82,8 @@ impl<'a, 'gcx, 'tcx> TraitDef<'tcx> { paren_sugar: bool, generics: &'tcx ty::Generics<'tcx>, trait_ref: ty::TraitRef<'tcx>, - associated_type_names: Vec) + associated_type_names: Vec, + def_path_hash: u64) -> TraitDef<'tcx> { TraitDef { paren_sugar: paren_sugar, @@ -90,6 +95,7 @@ impl<'a, 'gcx, 'tcx> TraitDef<'tcx> { blanket_impls: RefCell::new(vec![]), flags: Cell::new(ty::TraitFlags::NO_TRAIT_FLAGS), specialization_graph: RefCell::new(traits::specialization_graph::Graph::new()), + def_path_hash: def_path_hash, } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 6b3ebaa895fa3..d34fdaa7d71cd 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -411,15 +411,11 @@ impl<'a, 'gcx, 'tcx> TypeIdHasher<'a, 'gcx, 'tcx> { } fn def_id(&mut self, did: DefId) { - // Hash the crate identification information. - let name = self.tcx.crate_name(did.krate); - let disambiguator = self.tcx.crate_disambiguator(did.krate); - self.hash((name, disambiguator)); - - // Hash the item path within that crate. - // FIXME(#35379) This should use a deterministic - // DefPath hashing mechanism, not the DefIndex. - self.hash(did.index); + // Hash the DefPath corresponding to the DefId, which is independent + // of compiler internal state. + let tcx = self.tcx; + let def_path = tcx.def_path(did); + def_path.deterministic_hash_to(tcx, &mut self.state); } } @@ -445,33 +441,8 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> { self.hash(f.sig.variadic()); } TyTrait(ref data) => { - // Trait objects have a list of projection bounds - // that are not guaranteed to be sorted in an order - // that gets preserved across crates, so we need - // to sort them again by the name, in string form. - - // Hash the whole principal trait ref. self.def_id(data.principal.def_id()); - data.principal.visit_with(self); - - // Hash region and builtin bounds. - data.region_bound.visit_with(self); self.hash(data.builtin_bounds); - - // Only projection bounds are left, sort and hash them. - let mut projection_bounds: Vec<_> = data.projection_bounds - .iter() - .map(|b| (b.item_name().as_str(), b)) - .collect(); - projection_bounds.sort_by_key(|&(ref name, _)| name.clone()); - for (name, bound) in projection_bounds { - self.def_id(bound.0.trait_ref.def_id); - self.hash(name); - bound.visit_with(self); - } - - // Bypass super_visit_with, we've visited everything. - return false; } TyTuple(tys) => { self.hash(tys.len()); diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ba6c4b9b84c37..36e9fccdf5fd8 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -50,6 +50,7 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use syntax::{ast, diagnostics, visit}; use syntax::attr; +use syntax::ext::base::ExtCtxt; use syntax::parse::{self, PResult, token}; use syntax::util::node_count::NodeCounter; use syntax; @@ -638,6 +639,13 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, } sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?; + let mut macro_loader = + macro_import::MacroLoader::new(sess, &cstore, crate_name, krate.config.clone()); + + let resolver_arenas = Resolver::arenas(); + let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas); + syntax_ext::register_builtins(&mut resolver, sess.features.borrow().quote); + krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their // dependencies. It's up to us to tell the system where to find all the @@ -672,25 +680,17 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, }; - let mut loader = macro_import::MacroLoader::new(sess, - &cstore, - crate_name, - krate.config.clone()); - let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess, - krate.config.clone(), - cfg, - &mut loader); - syntax_ext::register_builtins(&mut ecx.syntax_env); + let mut ecx = ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, &mut resolver); let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate); if cfg!(windows) { env::set_var("PATH", &old_path); } - *sess.available_macros.borrow_mut() = ecx.syntax_env.names; ret }); krate = time(time_passes, "maybe building test harness", || { syntax::test::modify_for_testing(&sess.parse_sess, + &mut resolver, sess.opts.test, krate, sess.diagnostic()) @@ -701,6 +701,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro); let num_crate_types = crate_types.len(); syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess, + &mut resolver, krate, is_rustc_macro_crate, num_crate_types, @@ -708,11 +709,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, &sess.features.borrow()) }); - let resolver_arenas = Resolver::arenas(); - let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas); - - let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate)); - if sess.opts.debugging_opts.input_stats { println!("Post-expansion node count: {}", count_nodes(&krate)); } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index a6049acdb10d4..e8d9e90456efc 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -569,7 +569,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ty::TyTuple(_) => { FfiUnsafe("found Rust tuple type in foreign module; \ - consider using a struct instead`") + consider using a struct instead") } ty::TyRawPtr(ref m) | ty::TyRef(_, ref m) => { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d2840fbe4fe46..624bffb7e0369 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -385,12 +385,14 @@ pub fn get_trait_def<'a, 'tcx>(cdata: Cmd, let unsafety = parse_unsafety(item_doc); let associated_type_names = parse_associated_type_names(item_doc); let paren_sugar = parse_paren_sugar(item_doc); + let def_path = def_path(cdata, item_id).unwrap(); ty::TraitDef::new(unsafety, paren_sugar, generics, item_trait_ref(item_doc, tcx, cdata), - associated_type_names) + associated_type_names, + def_path.deterministic_hash(tcx)) } pub fn get_adt_def<'a, 'tcx>(cdata: Cmd, diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs index 22691975050e5..e41f076d64a80 100644 --- a/src/librustc_metadata/macro_import.rs +++ b/src/librustc_metadata/macro_import.rs @@ -18,6 +18,7 @@ use creader::{CrateReader, Macros}; use cstore::CStore; use rustc::hir::def_id::DefIndex; +use rustc::middle; use rustc::session::Session; use rustc::util::nodemap::FnvHashMap; use rustc_back::dynamic_lib::DynamicLibrary; @@ -26,7 +27,6 @@ use rustc_macro::__internal::Registry; use syntax::ast; use syntax::attr; use syntax::ext::base::LoadedMacro; -use syntax::ext; use syntax::parse::token; use syntax_ext::deriving::custom::CustomDerive; use syntax_pos::Span; @@ -55,7 +55,7 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) { pub type MacroSelection = FnvHashMap; -impl<'a> ext::base::MacroLoader for MacroLoader<'a> { +impl<'a> middle::cstore::MacroLoader for MacroLoader<'a> { fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs index 8030abf6330e7..dbefd3eacc24a 100644 --- a/src/librustc_metadata/tyencode.rs +++ b/src/librustc_metadata/tyencode.rs @@ -104,14 +104,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx enc_region(w, cx, obj.region_bound); - // Encode projection_bounds in a stable order - let mut projection_bounds: Vec<_> = obj.projection_bounds - .iter() - .map(|b| (b.item_name().as_str(), b)) - .collect(); - projection_bounds.sort_by_key(|&(ref name, _)| name.clone()); - - for tp in projection_bounds.iter().map(|&(_, tp)| tp) { + for tp in &obj.projection_bounds { write!(w, "P"); enc_existential_projection(w, cx, &tp.0); } diff --git a/src/librustc_resolve/assign_ids.rs b/src/librustc_resolve/assign_ids.rs deleted file mode 100644 index a9e3c6ffe9ed8..0000000000000 --- a/src/librustc_resolve/assign_ids.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use Resolver; -use rustc::session::Session; -use rustc::util::nodemap::FnvHashMap; -use syntax::ast; -use syntax::ext::hygiene::Mark; -use syntax::fold::{self, Folder}; -use syntax::ptr::P; -use syntax::util::move_map::MoveMap; -use syntax::util::small_vector::SmallVector; - -use std::mem; - -impl<'a> Resolver<'a> { - pub fn assign_node_ids(&mut self, krate: ast::Crate) -> ast::Crate { - NodeIdAssigner { - sess: self.session, - macros_at_scope: &mut self.macros_at_scope, - }.fold_crate(krate) - } -} - -struct NodeIdAssigner<'a> { - sess: &'a Session, - macros_at_scope: &'a mut FnvHashMap>, -} - -impl<'a> Folder for NodeIdAssigner<'a> { - fn new_id(&mut self, old_id: ast::NodeId) -> ast::NodeId { - assert_eq!(old_id, ast::DUMMY_NODE_ID); - self.sess.next_node_id() - } - - fn fold_block(&mut self, block: P) -> P { - block.map(|mut block| { - block.id = self.new_id(block.id); - - let stmt = block.stmts.pop(); - let mut macros = Vec::new(); - block.stmts = block.stmts.move_flat_map(|stmt| { - if let ast::StmtKind::Item(ref item) = stmt.node { - if let ast::ItemKind::Mac(..) = item.node { - macros.push(item.ident.ctxt.data().outer_mark); - return None; - } - } - - let stmt = self.fold_stmt(stmt).pop().unwrap(); - if !macros.is_empty() { - self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new())); - } - Some(stmt) - }); - - stmt.and_then(|mut stmt| { - // Avoid wasting a node id on a trailing expression statement, - // which shares a HIR node with the expression itself. - if let ast::StmtKind::Expr(expr) = stmt.node { - let expr = self.fold_expr(expr); - stmt.id = expr.id; - stmt.node = ast::StmtKind::Expr(expr); - Some(stmt) - } else { - self.fold_stmt(stmt).pop() - } - }).map(|stmt| { - if !macros.is_empty() { - self.macros_at_scope.insert(stmt.id, mem::replace(&mut macros, Vec::new())); - } - block.stmts.push(stmt); - }); - - block - }) - } - - fn fold_item(&mut self, item: P) -> SmallVector> { - match item.node { - ast::ItemKind::Mac(..) => SmallVector::zero(), - _ => fold::noop_fold_item(item, self), - } - } -} diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c5b505fba38e9..912b39cafff36 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -45,6 +45,7 @@ use self::ParentLink::*; use rustc::hir::map::Definitions; use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr}; +use rustc::middle::cstore::MacroLoader; use rustc::session::Session; use rustc::lint; use rustc::hir::def::*; @@ -79,10 +80,10 @@ use resolve_imports::{ImportDirective, NameResolution}; // registered before they are used. mod diagnostics; +mod macros; mod check_unused; mod build_reduced_graph; mod resolve_imports; -mod assign_ids; enum SuggestionType { Macro(String), @@ -247,7 +248,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, "method `{}` is not a member of trait `{}`", method, trait_); - err.span_label(span, &format!("not a member of `{}`", trait_)); + err.span_label(span, &format!("not a member of trait `{}`", trait_)); err } ResolutionError::TypeNotMemberOfTrait(type_, trait_) => { @@ -257,7 +258,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, "type `{}` is not a member of trait `{}`", type_, trait_); - err.span_label(span, &format!("not a member of trait `Foo`")); + err.span_label(span, &format!("not a member of trait `{}`", trait_)); err } ResolutionError::ConstNotMemberOfTrait(const_, trait_) => { @@ -267,7 +268,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>, "const `{}` is not a member of trait `{}`", const_, trait_); - err.span_label(span, &format!("not a member of trait `Foo`")); + err.span_label(span, &format!("not a member of trait `{}`", trait_)); err } ResolutionError::VariableNotBoundInPattern(variable_name, from, to) => { @@ -1068,6 +1069,12 @@ pub struct Resolver<'a> { arenas: &'a ResolverArenas<'a>, dummy_binding: &'a NameBinding<'a>, new_import_semantics: bool, // true if `#![feature(item_like_imports)]` + + macro_loader: &'a mut MacroLoader, + macro_names: FnvHashSet, + + // Maps the `Mark` of an expansion to its containing module or block. + expansion_data: Vec, } pub struct ResolverArenas<'a> { @@ -1166,7 +1173,10 @@ impl Named for hir::PathSegment { } impl<'a> Resolver<'a> { - pub fn new(session: &'a Session, make_glob_map: MakeGlobMap, arenas: &'a ResolverArenas<'a>) + pub fn new(session: &'a Session, + make_glob_map: MakeGlobMap, + macro_loader: &'a mut MacroLoader, + arenas: &'a ResolverArenas<'a>) -> Resolver<'a> { let root_def_id = DefId::local(CRATE_DEF_INDEX); let graph_root = @@ -1227,6 +1237,10 @@ impl<'a> Resolver<'a> { vis: ty::Visibility::Public, }), new_import_semantics: session.features.borrow().item_like_imports, + + macro_loader: macro_loader, + macro_names: FnvHashSet(), + expansion_data: vec![macros::ExpansionData::default()], } } @@ -2768,8 +2782,7 @@ impl<'a> Resolver<'a> { } fn find_best_match(&mut self, name: &str) -> SuggestionType { - if let Some(macro_name) = self.session.available_macros - .borrow().iter().find(|n| n.as_str() == name) { + if let Some(macro_name) = self.macro_names.iter().find(|n| n.as_str() == name) { return SuggestionType::Macro(format!("{}!", macro_name)); } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs new file mode 100644 index 0000000000000..67ee4c307d3c3 --- /dev/null +++ b/src/librustc_resolve/macros.rs @@ -0,0 +1,217 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use Resolver; +use rustc::util::nodemap::FnvHashMap; +use std::cell::RefCell; +use std::mem; +use std::rc::Rc; +use syntax::ast::{self, Name}; +use syntax::errors::DiagnosticBuilder; +use syntax::ext::base::{self, LoadedMacro, MultiModifier, MultiDecorator}; +use syntax::ext::base::{NormalTT, SyntaxExtension}; +use syntax::ext::expand::{Expansion, Invocation, InvocationKind}; +use syntax::ext::hygiene::Mark; +use syntax::parse::token::intern; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::visit::{self, Visitor}; + +#[derive(Clone, Default)] +pub struct ExpansionData { + module: Rc, +} + +// FIXME(jseyfried): merge with `::ModuleS`. +#[derive(Default)] +struct ModuleData { + parent: Option>, + macros: RefCell>>, + macros_escape: bool, +} + +impl<'a> base::Resolver for Resolver<'a> { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { + self.macro_loader.load_crate(extern_crate, allows_macros) + } + + fn next_node_id(&mut self) -> ast::NodeId { + self.session.next_node_id() + } + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { + expansion.visit_with(&mut ExpansionVisitor { + current_module: self.expansion_data[mark.as_u32() as usize].module.clone(), + resolver: self, + }); + } + + fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc) { + if let NormalTT(..) = *ext { + self.macro_names.insert(ident.name); + } + + let mut module = self.expansion_data[scope.as_u32() as usize].module.clone(); + while module.macros_escape { + module = module.parent.clone().unwrap(); + } + module.macros.borrow_mut().insert(ident.name, ext); + } + + fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec) { + self.macros_at_scope.insert(id, macros); + } + + fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option { + for i in 0..attrs.len() { + let name = intern(&attrs[i].name()); + match self.expansion_data[0].module.macros.borrow().get(&name) { + Some(ext) => match **ext { + MultiModifier(..) | MultiDecorator(..) => return Some(attrs.remove(i)), + _ => {} + }, + None => {} + } + } + None + } + + fn resolve_invoc(&mut self, invoc: &Invocation) -> Option> { + let (name, span) = match invoc.kind { + InvocationKind::Bang { ref mac, .. } => { + let path = &mac.node.path; + if path.segments.len() > 1 || path.global || + !path.segments[0].parameters.is_empty() { + self.session.span_err(path.span, + "expected macro name without module separators"); + return None; + } + (path.segments[0].identifier.name, path.span) + } + InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span), + }; + + let mut module = self.expansion_data[invoc.mark().as_u32() as usize].module.clone(); + loop { + if let Some(ext) = module.macros.borrow().get(&name) { + return Some(ext.clone()); + } + match module.parent.clone() { + Some(parent) => module = parent, + None => break, + } + } + + let mut err = + self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name)); + self.suggest_macro_name(&name.as_str(), &mut err); + err.emit(); + None + } +} + +impl<'a> Resolver<'a> { + fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) { + if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) { + if suggestion != name { + err.help(&format!("did you mean `{}!`?", suggestion)); + } else { + err.help(&format!("have you added the `#[macro_use]` on the module/import?")); + } + } + } +} + +struct ExpansionVisitor<'b, 'a: 'b> { + resolver: &'b mut Resolver<'a>, + current_module: Rc, +} + +impl<'a, 'b> ExpansionVisitor<'a, 'b> { + fn visit_invoc(&mut self, id: ast::NodeId) { + assert_eq!(id, self.resolver.expansion_data.len() as u32); + self.resolver.expansion_data.push(ExpansionData { + module: self.current_module.clone(), + }); + } + + // does this attribute list contain "macro_use"? + fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { + for attr in attrs { + if attr.check_name("macro_escape") { + let msg = "macro_escape is a deprecated synonym for macro_use"; + let mut err = self.resolver.session.struct_span_warn(attr.span, msg); + if let ast::AttrStyle::Inner = attr.node.style { + err.help("consider an outer attribute, #[macro_use] mod ...").emit(); + } else { + err.emit(); + } + } else if !attr.check_name("macro_use") { + continue; + } + + if !attr.is_word() { + self.resolver.session.span_err(attr.span, + "arguments to macro_use are not allowed here"); + } + return true; + } + + false + } +} + +macro_rules! method { + ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => { + fn $visit(&mut self, node: &$ty) { + match node.node { + $invoc(..) => self.visit_invoc(node.id), + _ => visit::$walk(self, node), + } + } + } +} + +impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b> { + method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item); + method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item); + method!(visit_stmt: ast::Stmt, ast::StmtKind::Mac, walk_stmt); + method!(visit_expr: ast::Expr, ast::ExprKind::Mac, walk_expr); + method!(visit_pat: ast::Pat, ast::PatKind::Mac, walk_pat); + method!(visit_ty: ast::Ty, ast::TyKind::Mac, walk_ty); + + fn visit_item(&mut self, item: &ast::Item) { + match item.node { + ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder + ast::ItemKind::Mac(..) => self.visit_invoc(item.id), + ast::ItemKind::Mod(..) => { + let module_data = ModuleData { + parent: Some(self.current_module.clone()), + macros: RefCell::new(FnvHashMap()), + macros_escape: self.contains_macro_use(&item.attrs), + }; + let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); + visit::walk_item(self, item); + self.current_module = orig_module; + } + _ => visit::walk_item(self, item), + } + } + + fn visit_block(&mut self, block: &ast::Block) { + let module_data = ModuleData { + parent: Some(self.current_module.clone()), + macros: RefCell::new(FnvHashMap()), + macros_escape: false, + }; + let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); + visit::walk_block(self, block); + self.current_module = orig_module; + } +} diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 6b48c6ae26dac..3073b1dbfaeeb 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -296,6 +296,7 @@ fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, sized_args = [v0]; &sized_args } else { + // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments unsized_args = [ Load(bcx, get_dataptr(bcx, v0)), Load(bcx, get_meta(bcx, v0)) @@ -440,7 +441,9 @@ pub fn size_and_align_of_dst<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>, } } -fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueKind<'tcx>) +fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + v0: ValueRef, + g: DropGlueKind<'tcx>) -> Block<'blk, 'tcx> { let t = g.ty(); @@ -463,6 +466,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK let llval = get_dataptr(bcx, v0); let llbox = Load(bcx, llval); let bcx = drop_ty(bcx, v0, content_ty, DebugLoc::None); + // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments let info = get_meta(bcx, v0); let info = Load(bcx, info); let (llsize, llalign) = @@ -488,6 +492,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK // No support in vtable for distinguishing destroying with // versus without calling Drop::drop. Assert caller is // okay with always calling the Drop impl, if any. + // FIXME(#36457) -- we should pass unsized values to drop glue as two arguments assert!(!skip_dtor); let data_ptr = get_dataptr(bcx, v0); let vtable_ptr = Load(bcx, get_meta(bcx, v0)); @@ -543,6 +548,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, let value = if type_is_sized(cx.tcx(), t) { adt::MaybeSizedValue::sized(av) } else { + // FIXME(#36457) -- we should pass unsized values as two arguments let data = Load(cx, get_dataptr(cx, av)); let info = Load(cx, get_meta(cx, av)); adt::MaybeSizedValue::unsized_(data, info) @@ -586,6 +592,7 @@ fn drop_structural_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, let val = if type_is_sized(cx.tcx(), field_ty) { llfld_a } else { + // FIXME(#36457) -- we should pass unsized values as two arguments let scratch = alloc_ty(cx, field_ty, "__fat_ptr_iter"); Store(cx, llfld_a, get_dataptr(cx, scratch)); Store(cx, value.meta, get_meta(cx, scratch)); diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index 694db035e282c..0d919cb775701 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -194,6 +194,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let ptr = if is_sized { llargs[0] } else { + // FIXME(#36457) -- we should pass unsized values as two arguments let scratch = alloc_ty(bcx, tp_ty, "drop"); call_lifetime_start(bcx, scratch); Store(bcx, llargs[0], get_dataptr(bcx, scratch)); diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index fbd04d7b38029..baeafbe3e346f 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -242,10 +242,28 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let lvalue = self.trans_lvalue(&bcx, location); let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); let drop_ty = glue::get_drop_glue_type(bcx.tcx(), ty); - let llvalue = if drop_ty != ty { - bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) + let is_sized = common::type_is_sized(bcx.tcx(), ty); + let llvalue = if is_sized { + if drop_ty != ty { + bcx.pointercast(lvalue.llval, type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) + } else { + lvalue.llval + } } else { - lvalue.llval + // FIXME(#36457) Currently drop glue takes sized + // values as a `*(data, meta)`, but elsewhere in + // MIR we pass `(data, meta)` as two separate + // arguments. It would be better to fix drop glue, + // but I am shooting for a quick fix to #35546 + // here that can be cleanly backported to beta, so + // I want to avoid touching all of trans. + bcx.with_block(|bcx| { + let scratch = base::alloc_ty(bcx, ty, "drop"); + base::call_lifetime_start(bcx, scratch); + build::Store(bcx, lvalue.llval, base::get_dataptr(bcx, scratch)); + build::Store(bcx, lvalue.llextra, base::get_meta(bcx, scratch)); + scratch + }) }; if let Some(unwind) = unwind { bcx.invoke(drop_fn, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7d111cdc4156f..04aca8c0947ca 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1290,12 +1290,15 @@ fn trait_def_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, } }).collect(); + let def_path_hash = tcx.def_path(def_id).deterministic_hash(tcx); + let trait_ref = ty::TraitRef::new(def_id, substs); let trait_def = ty::TraitDef::new(unsafety, paren_sugar, ty_generics, trait_ref, - associated_type_names); + associated_type_names, + def_path_hash); tcx.intern_trait_def(trait_def) } diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 79bbe5e7daa45..246c57ab23871 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -97,6 +97,146 @@ impl Duration { #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn subsec_nanos(&self) -> u32 { self.nanos } + + /// Checked duration addition. Computes `self + other`, returning `None` + /// if overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(duration_checked_ops)] + /// + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), Some(Duration::new(0, 1))); + /// assert_eq!(Duration::new(1, 0).checked_add(Duration::new(std::u64::MAX, 0)), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_add(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_add(rhs.secs) { + let mut nanos = self.nanos + rhs.nanos; + if nanos >= NANOS_PER_SEC { + nanos -= NANOS_PER_SEC; + if let Some(new_secs) = secs.checked_add(1) { + secs = new_secs; + } else { + return None; + } + } + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { + secs: secs, + nanos: nanos, + }) + } else { + None + } + } + + /// Checked duration subtraction. Computes `self + other`, returning `None` + /// if the result would be negative or if underflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(duration_checked_ops)] + /// + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 1).checked_sub(Duration::new(0, 0)), Some(Duration::new(0, 1))); + /// assert_eq!(Duration::new(0, 0).checked_sub(Duration::new(0, 1)), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_sub(self, rhs: Duration) -> Option { + if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { + let nanos = if self.nanos >= rhs.nanos { + self.nanos - rhs.nanos + } else { + if let Some(sub_secs) = secs.checked_sub(1) { + secs = sub_secs; + self.nanos + NANOS_PER_SEC - rhs.nanos + } else { + return None; + } + }; + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs: secs, nanos: nanos }) + } else { + None + } + } + + /// Checked duration multiplication. Computes `self * other`, returning + /// `None` if underflow or overflow occurred. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(duration_checked_ops)] + /// + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 500_000_001).checked_mul(2), Some(Duration::new(1, 2))); + /// assert_eq!(Duration::new(std::u64::MAX - 1, 0).checked_mul(2), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_mul(self, rhs: u32) -> Option { + // Multiply nanoseconds as u64, because it cannot overflow that way. + let total_nanos = self.nanos as u64 * rhs as u64; + let extra_secs = total_nanos / (NANOS_PER_SEC as u64); + let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; + if let Some(secs) = self.secs + .checked_mul(rhs as u64) + .and_then(|s| s.checked_add(extra_secs)) { + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { + secs: secs, + nanos: nanos, + }) + } else { + None + } + } + + /// Checked duration division. Computes `self / other`, returning `None` + /// if `other == 0` or the operation results in underflow or overflow. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(duration_checked_ops)] + /// + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); + /// assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); + /// assert_eq!(Duration::new(2, 0).checked_div(0), None); + /// ``` + #[unstable(feature = "duration_checked_ops", issue = "35774")] + #[inline] + pub fn checked_div(self, rhs: u32) -> Option { + if rhs != 0 { + let secs = self.secs / (rhs as u64); + let carry = self.secs - secs * (rhs as u64); + let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); + let nanos = self.nanos / rhs + (extra_nanos as u32); + debug_assert!(nanos < NANOS_PER_SEC); + Some(Duration { secs: secs, nanos: nanos }) + } else { + None + } + } } #[stable(feature = "duration", since = "1.3.0")] @@ -104,15 +244,7 @@ impl Add for Duration { type Output = Duration; fn add(self, rhs: Duration) -> Duration { - let mut secs = self.secs.checked_add(rhs.secs) - .expect("overflow when adding durations"); - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs = secs.checked_add(1).expect("overflow when adding durations"); - } - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_add(rhs).expect("overflow when adding durations") } } @@ -128,17 +260,7 @@ impl Sub for Duration { type Output = Duration; fn sub(self, rhs: Duration) -> Duration { - let mut secs = self.secs.checked_sub(rhs.secs) - .expect("overflow when subtracting durations"); - let nanos = if self.nanos >= rhs.nanos { - self.nanos - rhs.nanos - } else { - secs = secs.checked_sub(1) - .expect("overflow when subtracting durations"); - self.nanos + NANOS_PER_SEC - rhs.nanos - }; - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_sub(rhs).expect("overflow when subtracting durations") } } @@ -154,15 +276,7 @@ impl Mul for Duration { type Output = Duration; fn mul(self, rhs: u32) -> Duration { - // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos as u64 * rhs as u64; - let extra_secs = total_nanos / (NANOS_PER_SEC as u64); - let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; - let secs = self.secs.checked_mul(rhs as u64) - .and_then(|s| s.checked_add(extra_secs)) - .expect("overflow when multiplying duration"); - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_mul(rhs).expect("overflow when multiplying duration by scalar") } } @@ -178,12 +292,7 @@ impl Div for Duration { type Output = Duration; fn div(self, rhs: u32) -> Duration { - let secs = self.secs / (rhs as u64); - let carry = self.secs - secs * (rhs as u64); - let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); - let nanos = self.nanos / rhs + (extra_nanos as u32); - debug_assert!(nanos < NANOS_PER_SEC); - Duration { secs: secs, nanos: nanos } + self.checked_div(rhs).expect("divide by zero error when dividing duration by scalar") } } @@ -234,6 +343,15 @@ mod tests { Duration::new(1, 1)); } + #[test] + fn checked_add() { + assert_eq!(Duration::new(0, 0).checked_add(Duration::new(0, 1)), + Some(Duration::new(0, 1))); + assert_eq!(Duration::new(0, 500_000_000).checked_add(Duration::new(0, 500_000_001)), + Some(Duration::new(1, 1))); + assert_eq!(Duration::new(1, 0).checked_add(Duration::new(::u64::MAX, 0)), None); + } + #[test] fn sub() { assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), @@ -244,6 +362,18 @@ mod tests { Duration::new(0, 999_999_999)); } + #[test] + fn checked_sub() { + let zero = Duration::new(0, 0); + let one_nano = Duration::new(0, 1); + let one_sec = Duration::new(1, 0); + assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1))); + assert_eq!(one_sec.checked_sub(one_nano), + Some(Duration::new(0, 999_999_999))); + assert_eq!(zero.checked_sub(one_nano), None); + assert_eq!(zero.checked_sub(one_sec), None); + } + #[test] #[should_panic] fn sub_bad1() { Duration::new(0, 0) - Duration::new(0, 1); @@ -263,6 +393,16 @@ mod tests { Duration::new(2000, 4000)); } + #[test] + fn checked_mul() { + assert_eq!(Duration::new(0, 1).checked_mul(2), Some(Duration::new(0, 2))); + assert_eq!(Duration::new(1, 1).checked_mul(3), Some(Duration::new(3, 3))); + assert_eq!(Duration::new(0, 500_000_001).checked_mul(4), Some(Duration::new(2, 4))); + assert_eq!(Duration::new(0, 500_000_001).checked_mul(4000), + Some(Duration::new(2000, 4000))); + assert_eq!(Duration::new(::u64::MAX - 1, 0).checked_mul(2), None); + } + #[test] fn div() { assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0)); @@ -270,4 +410,11 @@ mod tests { assert_eq!(Duration::new(99, 999_999_000) / 100, Duration::new(0, 999_999_990)); } + + #[test] + fn checked_div() { + assert_eq!(Duration::new(2, 0).checked_div(2), Some(Duration::new(1, 0))); + assert_eq!(Duration::new(1, 0).checked_div(2), Some(Duration::new(0, 500_000_000))); + assert_eq!(Duration::new(2, 0).checked_div(0), None); + } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index edd38ea23e2fd..fb4816d3847ed 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -10,27 +10,25 @@ pub use self::SyntaxExtension::*; -use ast; -use ast::{Name, PatKind}; +use ast::{self, Attribute, Name, PatKind}; use attr::HasAttrs; -use codemap::{self, CodeMap, ExpnInfo}; +use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; use errors::DiagnosticBuilder; -use ext; -use ext::expand; +use ext::expand::{self, Invocation, Expansion}; +use ext::hygiene::Mark; use ext::tt::macro_rules; use parse; use parse::parser; use parse::token; -use parse::token::{InternedString, intern, str_to_ident}; +use parse::token::{InternedString, str_to_ident}; use ptr::P; use std_inject; use util::small_vector::SmallVector; -use util::lev_distance::find_best_match_for_name; use fold::Folder; use feature_gate; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; use tokenstream; @@ -44,7 +42,7 @@ pub enum Annotatable { } impl HasAttrs for Annotatable { - fn attrs(&self) -> &[ast::Attribute] { + fn attrs(&self) -> &[Attribute] { match *self { Annotatable::Item(ref item) => &item.attrs, Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, @@ -52,7 +50,7 @@ impl HasAttrs for Annotatable { } } - fn map_attrs) -> Vec>(self, f: F) -> Self { + fn map_attrs) -> Vec>(self, f: F) -> Self { match self { Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)), Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)), @@ -464,91 +462,16 @@ pub enum SyntaxExtension { pub type NamedSyntaxExtension = (Name, SyntaxExtension); -/// The base map of methods for expanding syntax extension -/// AST nodes into full ASTs -fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) - -> SyntaxEnv { - // utility function to simplify creating NormalTT syntax extensions - fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { - NormalTT(Box::new(f), None, false) - } - - let mut syntax_expanders = SyntaxEnv::new(); - syntax_expanders.insert(intern("macro_rules"), MacroRulesTT); - - if ecfg.enable_quotes() { - // Quasi-quoting expanders - syntax_expanders.insert(intern("quote_tokens"), - builtin_normal_expander( - ext::quote::expand_quote_tokens)); - syntax_expanders.insert(intern("quote_expr"), - builtin_normal_expander( - ext::quote::expand_quote_expr)); - syntax_expanders.insert(intern("quote_ty"), - builtin_normal_expander( - ext::quote::expand_quote_ty)); - syntax_expanders.insert(intern("quote_item"), - builtin_normal_expander( - ext::quote::expand_quote_item)); - syntax_expanders.insert(intern("quote_pat"), - builtin_normal_expander( - ext::quote::expand_quote_pat)); - syntax_expanders.insert(intern("quote_arm"), - builtin_normal_expander( - ext::quote::expand_quote_arm)); - syntax_expanders.insert(intern("quote_stmt"), - builtin_normal_expander( - ext::quote::expand_quote_stmt)); - syntax_expanders.insert(intern("quote_matcher"), - builtin_normal_expander( - ext::quote::expand_quote_matcher)); - syntax_expanders.insert(intern("quote_attr"), - builtin_normal_expander( - ext::quote::expand_quote_attr)); - syntax_expanders.insert(intern("quote_arg"), - builtin_normal_expander( - ext::quote::expand_quote_arg)); - syntax_expanders.insert(intern("quote_block"), - builtin_normal_expander( - ext::quote::expand_quote_block)); - syntax_expanders.insert(intern("quote_meta_item"), - builtin_normal_expander( - ext::quote::expand_quote_meta_item)); - syntax_expanders.insert(intern("quote_path"), - builtin_normal_expander( - ext::quote::expand_quote_path)); - } - - syntax_expanders.insert(intern("line"), - builtin_normal_expander( - ext::source_util::expand_line)); - syntax_expanders.insert(intern("column"), - builtin_normal_expander( - ext::source_util::expand_column)); - syntax_expanders.insert(intern("file"), - builtin_normal_expander( - ext::source_util::expand_file)); - syntax_expanders.insert(intern("stringify"), - builtin_normal_expander( - ext::source_util::expand_stringify)); - syntax_expanders.insert(intern("include"), - builtin_normal_expander( - ext::source_util::expand_include)); - syntax_expanders.insert(intern("include_str"), - builtin_normal_expander( - ext::source_util::expand_include_str)); - syntax_expanders.insert(intern("include_bytes"), - builtin_normal_expander( - ext::source_util::expand_include_bytes)); - syntax_expanders.insert(intern("module_path"), - builtin_normal_expander( - ext::source_util::expand_mod)); - syntax_expanders -} - -pub trait MacroLoader { - fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) - -> Vec; +pub trait Resolver { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; + fn next_node_id(&mut self) -> ast::NodeId; + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); + fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc); + fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec); + + fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option; + fn resolve_invoc(&mut self, invoc: &Invocation) -> Option>; } pub enum LoadedMacro { @@ -556,11 +479,35 @@ pub enum LoadedMacro { CustomDerive(String, Box), } -pub struct DummyMacroLoader; -impl MacroLoader for DummyMacroLoader { - fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec { +pub struct DummyResolver; + +impl Resolver for DummyResolver { + fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec { Vec::new() } + fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } + + fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} + fn add_macro(&mut self, _scope: Mark, _ident: ast::Ident, _ext: Rc) {} + fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec) {} + + fn find_attr_invoc(&mut self, _attrs: &mut Vec) -> Option { None } + fn resolve_invoc(&mut self, _invoc: &Invocation) -> Option> { None } +} + +#[derive(Clone)] +pub struct ModuleData { + pub mod_path: Vec, + pub directory: PathBuf, +} + +#[derive(Clone)] +pub struct ExpansionData { + pub mark: Mark, + pub depth: usize, + pub backtrace: ExpnId, + pub module: Rc, + pub in_block: bool, } /// One of these is made during expansion and incrementally updated as we go; @@ -569,63 +516,68 @@ impl MacroLoader for DummyMacroLoader { pub struct ExtCtxt<'a> { pub parse_sess: &'a parse::ParseSess, pub cfg: ast::CrateConfig, - pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, - pub loader: &'a mut MacroLoader, - + pub resolver: &'a mut Resolver, pub exported_macros: Vec, - - pub syntax_env: SyntaxEnv, pub derive_modes: HashMap>, - pub recursion_count: usize, + pub current_expansion: ExpansionData, } impl<'a> ExtCtxt<'a> { pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, ecfg: expand::ExpansionConfig<'a>, - loader: &'a mut MacroLoader) + resolver: &'a mut Resolver) -> ExtCtxt<'a> { ExtCtxt { - syntax_env: initial_syntax_expander_table(&ecfg), parse_sess: parse_sess, cfg: cfg, - backtrace: NO_EXPANSION, ecfg: ecfg, crate_root: None, exported_macros: Vec::new(), - loader: loader, + resolver: resolver, derive_modes: HashMap::new(), - recursion_count: 0, + current_expansion: ExpansionData { + mark: Mark::root(), + depth: 0, + backtrace: NO_EXPANSION, + module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), + in_block: false, + }, } } /// Returns a `Folder` for deeply expanding all macros in an AST node. pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { - expand::MacroExpander::new(self, false, false) + expand::MacroExpander::new(self, false) + } + + /// Returns a `Folder` that deeply expands all macros and assigns all node ids in an AST node. + /// Once node ids are assigned, the node may not be expanded, removed, or otherwise modified. + pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { + expand::MacroExpander::new(self, true) } pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) -> parser::Parser<'a> { parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg()) } - pub fn codemap(&self) -> &'a CodeMap { self.parse_sess.codemap() } pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess } pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() } pub fn call_site(&self) -> Span { - self.codemap().with_expn_info(self.backtrace, |ei| match ei { + self.codemap().with_expn_info(self.backtrace(), |ei| match ei { Some(expn_info) => expn_info.call_site, None => self.bug("missing top span") }) } - pub fn backtrace(&self) -> ExpnId { self.backtrace } + pub fn backtrace(&self) -> ExpnId { self.current_expansion.backtrace } /// Returns span for the macro which originally caused the current expansion to happen. /// /// Stops backtracing at include! boundary. pub fn expansion_cause(&self) -> Span { - let mut expn_id = self.backtrace; + let mut expn_id = self.backtrace(); let mut last_macro = None; loop { if self.codemap().with_expn_info(expn_id, |info| { @@ -646,15 +598,15 @@ impl<'a> ExtCtxt<'a> { } pub fn bt_push(&mut self, ei: ExpnInfo) { - if self.recursion_count > self.ecfg.recursion_limit { + if self.current_expansion.depth > self.ecfg.recursion_limit { self.span_fatal(ei.call_site, &format!("recursion limit reached while expanding the macro `{}`", ei.callee.name())); } let mut call_site = ei.call_site; - call_site.expn_id = self.backtrace; - self.backtrace = self.codemap().record_expansion(ExpnInfo { + call_site.expn_id = self.backtrace(); + self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo { call_site: call_site, callee: ei.callee }); @@ -667,14 +619,11 @@ impl<'a> ExtCtxt<'a> { } if def.use_locally { let ext = macro_rules::compile(self, &def); - self.syntax_env.insert(def.ident.name, ext); + self.resolver.add_macro(self.current_expansion.mark, def.ident, Rc::new(ext)); } } - pub fn insert_custom_derive(&mut self, - name: &str, - ext: Box, - sp: Span) { + pub fn insert_custom_derive(&mut self, name: &str, ext: Box, sp: Span) { if !self.ecfg.enable_rustc_macro() { feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic, "rustc_macro", @@ -685,8 +634,7 @@ impl<'a> ExtCtxt<'a> { } let name = token::intern_and_get_ident(name); if self.derive_modes.insert(name.clone(), ext).is_some() { - self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", - name)); + self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name)); } } @@ -765,20 +713,6 @@ impl<'a> ExtCtxt<'a> { token::intern(st) } - pub fn suggest_macro_name(&mut self, - name: &str, - err: &mut DiagnosticBuilder<'a>) { - let names = &self.syntax_env.names; - if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) { - if suggestion != name { - err.help(&format!("did you mean `{}!`?", suggestion)); - } else { - err.help(&format!("have you added the `#[macro_use]` on the \ - module/import?")); - } - } - } - pub fn initialize(&mut self, user_exts: Vec, krate: &ast::Crate) { if std_inject::no_core(&krate) { self.crate_root = None; @@ -789,27 +723,27 @@ impl<'a> ExtCtxt<'a> { } for (name, extension) in user_exts { - self.syntax_env.insert(name, extension); + let ident = ast::Ident::with_empty_ctxt(name); + self.resolver.add_macro(Mark::root(), ident, Rc::new(extension)); } - self.syntax_env.current_module = Module(0); - let mut paths = ModulePaths { + let mut module = ModuleData { mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)], directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)), }; - paths.directory.pop(); - self.syntax_env.module_data[0].paths = Rc::new(paths); + module.directory.pop(); + self.current_expansion.module = Rc::new(module); } } /// Extract a string literal from the macro expanded version of `expr`, /// emitting `err_msg` if `expr` is not a string literal. This does not stop /// compilation on error, merely emits a non-fatal error and returns None. -pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) - -> Option<(InternedString, ast::StrStyle)> { +pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) + -> Option> { // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. let expr = expr.map(|mut expr| { - expr.span.expn_id = cx.backtrace; + expr.span.expn_id = cx.backtrace(); expr }); @@ -817,7 +751,7 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) let expr = cx.expander().fold_expr(expr); match expr.node { ast::ExprKind::Lit(ref l) => match l.node { - ast::LitKind::Str(ref s, style) => return Some(((*s).clone(), style)), + ast::LitKind::Str(ref s, style) => return Some(respan(expr.span, (s.clone(), style))), _ => cx.span_err(l.span, err_msg) }, _ => cx.span_err(expr.span, err_msg) @@ -825,6 +759,11 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) None } +pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) + -> Option<(InternedString, ast::StrStyle)> { + expr_to_spanned_string(cx, expr, err_msg).map(|s| s.node) +} + /// Non-fatally assert that `tts` is empty. Note that this function /// returns even when `tts` is non-empty, macros that *need* to stop /// compilation should call @@ -851,7 +790,7 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt, cx.span_err(sp, &format!("{} takes 1 argument", name)); return None } - let ret = cx.expander().fold_expr(panictry!(p.parse_expr())); + let ret = panictry!(p.parse_expr()); if p.token != token::Eof { cx.span_err(sp, &format!("{} takes 1 argument", name)); } @@ -879,104 +818,3 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, } Some(es) } - -/// In order to have some notion of scoping for macros, -/// we want to implement the notion of a transformation -/// environment. -/// -/// This environment maps Names to SyntaxExtensions. -pub struct SyntaxEnv { - module_data: Vec, - pub current_module: Module, - - /// All bang-style macro/extension names - /// encountered so far; to be used for diagnostics in resolve - pub names: HashSet, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct Module(u32); - -struct ModuleData { - parent: Module, - paths: Rc, - macros: HashMap>, - macros_escape: bool, - in_block: bool, -} - -#[derive(Clone)] -pub struct ModulePaths { - pub mod_path: Vec, - pub directory: PathBuf, -} - -impl SyntaxEnv { - fn new() -> SyntaxEnv { - let mut env = SyntaxEnv { - current_module: Module(0), - module_data: Vec::new(), - names: HashSet::new(), - }; - let paths = Rc::new(ModulePaths { mod_path: Vec::new(), directory: PathBuf::new() }); - env.add_module(false, false, paths); - env - } - - fn data(&self, module: Module) -> &ModuleData { - &self.module_data[module.0 as usize] - } - - pub fn paths(&self) -> Rc { - self.data(self.current_module).paths.clone() - } - - pub fn in_block(&self) -> bool { - self.data(self.current_module).in_block - } - - pub fn add_module(&mut self, macros_escape: bool, in_block: bool, paths: Rc) - -> Module { - let data = ModuleData { - parent: self.current_module, - paths: paths, - macros: HashMap::new(), - macros_escape: macros_escape, - in_block: in_block, - }; - - self.module_data.push(data); - Module(self.module_data.len() as u32 - 1) - } - - pub fn find(&self, name: Name) -> Option> { - let mut module = self.current_module; - let mut module_data; - loop { - module_data = self.data(module); - if let Some(ext) = module_data.macros.get(&name) { - return Some(ext.clone()); - } - if module == module_data.parent { - return None; - } - module = module_data.parent; - } - } - - pub fn insert(&mut self, name: Name, ext: SyntaxExtension) { - if let NormalTT(..) = ext { - self.names.insert(name); - } - - let mut module = self.current_module; - while self.data(module).macros_escape { - module = self.data(module).parent; - } - self.module_data[module.0 as usize].macros.insert(name, Rc::new(ext)); - } - - pub fn is_crate_root(&mut self) -> bool { - self.current_module == Module(0) - } -} diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 3dcdbc8909627..b81d95a6998c3 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -97,6 +97,7 @@ pub trait AstBuilder { typ: P, ex: P) -> P; + fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt; fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt; // blocks @@ -577,6 +578,23 @@ impl<'a> AstBuilder for ExtCtxt<'a> { }) } + // Generate `let _: Type;`, usually used for type assertions. + fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt { + let local = P(ast::Local { + pat: self.pat_wild(span), + ty: Some(ty), + init: None, + id: ast::DUMMY_NODE_ID, + span: span, + attrs: ast::ThinVec::new(), + }); + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Local(local), + span: span, + } + } + fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4715eda837490..62e299684b760 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -25,6 +25,7 @@ use parse::token::{intern, keywords}; use ptr::P; use tokenstream::TokenTree; use util::small_vector::SmallVector; +use visit::Visitor; use std::mem; use std::path::PathBuf; @@ -32,7 +33,8 @@ use std::rc::Rc; macro_rules! expansions { ($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident, - $(.$fold:ident)* $(lift .$fold_elt:ident)*;)*) => { + $(.$fold:ident)* $(lift .$fold_elt:ident)*, + $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { #[derive(Copy, Clone)] pub enum ExpansionKind { OptExpr, $( $kind, )* } pub enum Expansion { OptExpr(Option>), $( $kind($ty), )* } @@ -77,6 +79,17 @@ macro_rules! expansions { }, )*)* } } + + pub fn visit_with(&self, visitor: &mut V) { + match *self { + Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), + Expansion::OptExpr(None) => {} + $($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)* + $($( Expansion::$kind(ref ast) => for ast in ast.as_slice() { + visitor.$visit_elt(ast); + }, )*)* + } + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -94,17 +107,17 @@ macro_rules! expansions { } expansions! { - Expr: P [], "expression", .make_expr, .fold_expr; - Pat: P [], "pattern", .make_pat, .fold_pat; - Ty: P [], "type", .make_ty, .fold_ty; + Expr: P [], "expression", .make_expr, .fold_expr, .visit_expr; + Pat: P [], "pattern", .make_pat, .fold_pat, .visit_pat; + Ty: P [], "type", .make_ty, .fold_ty, .visit_ty; Stmts: SmallVector [SmallVector, ast::Stmt], - "statement", .make_stmts, lift .fold_stmt; + "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; Items: SmallVector> [SmallVector, P], - "item", .make_items, lift .fold_item; + "item", .make_items, lift .fold_item, lift .visit_item; TraitItems: SmallVector [SmallVector, ast::TraitItem], - "trait item", .make_trait_items, lift .fold_trait_item; + "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item; ImplItems: SmallVector [SmallVector, ast::ImplItem], - "impl item", .make_impl_items, lift .fold_impl_item; + "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item; } impl ExpansionKind { @@ -127,15 +140,12 @@ impl ExpansionKind { } pub struct Invocation { - kind: InvocationKind, + pub kind: InvocationKind, expansion_kind: ExpansionKind, - mark: Mark, - module: Module, - backtrace: ExpnId, - depth: usize, + expansion_data: ExpansionData, } -enum InvocationKind { +pub enum InvocationKind { Bang { attrs: Vec, mac: ast::Mac, @@ -148,29 +158,53 @@ enum InvocationKind { }, } +impl Invocation { + fn span(&self) -> Span { + match self.kind { + InvocationKind::Bang { span, .. } => span, + InvocationKind::Attr { ref attr, .. } => attr.span, + } + } + + pub fn mark(&self) -> Mark { + self.expansion_data.mark + } +} + pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, pub single_step: bool, pub keep_macs: bool, + monotonic: bool, // c.f. `cx.monotonic_expander()` } impl<'a, 'b> MacroExpander<'a, 'b> { - pub fn new(cx: &'a mut ExtCtxt<'b>, - single_step: bool, - keep_macs: bool) -> MacroExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { MacroExpander { cx: cx, - single_step: single_step, - keep_macs: keep_macs + monotonic: monotonic, + single_step: false, + keep_macs: false, } } fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { let err_count = self.cx.parse_sess.span_diagnostic.err_count(); - let items = Expansion::Items(SmallVector::many(krate.module.items)); - krate.module.items = self.expand(items).make_items().into(); - krate.exported_macros = self.cx.exported_macros.clone(); + let mut krate_item = placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) + .make_items().pop().unwrap().unwrap(); + krate_item.node = ast::ItemKind::Mod(krate.module); + let krate_item = Expansion::Items(SmallVector::one(P(krate_item))); + + krate.module = match self.expand(krate_item).make_items().pop().unwrap().unwrap().node { + ast::ItemKind::Mod(module) => module, + _ => unreachable!(), + }; + krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new()); + + for def in &mut krate.exported_macros { + def.id = self.cx.resolver.next_node_id() + } if self.cx.parse_sess.span_diagnostic.err_count() > err_count { self.cx.parse_sess.span_diagnostic.abort_if_errors(); @@ -181,21 +215,23 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Fully expand all the invocations in `expansion`. fn expand(&mut self, expansion: Expansion) -> Expansion { - self.cx.recursion_count = 0; + let orig_expansion_data = self.cx.current_expansion.clone(); + self.cx.current_expansion.depth = 0; + let (expansion, mut invocations) = self.collect_invocations(expansion); invocations.reverse(); let mut expansions = vec![vec![(0, expansion)]]; while let Some(invoc) = invocations.pop() { - let Invocation { mark, module, depth, backtrace, .. } = invoc; - self.cx.syntax_env.current_module = module; - self.cx.recursion_count = depth; - self.cx.backtrace = backtrace; + let ExpansionData { depth, mark, .. } = invoc.expansion_data; + self.cx.current_expansion = invoc.expansion_data.clone(); - let expansion = self.expand_invoc(invoc); + let expansion = match self.cx.resolver.resolve_invoc(&invoc) { + Some(ext) => self.expand_invoc(invoc, ext), + None => invoc.expansion_kind.dummy(invoc.span()), + }; - self.cx.syntax_env.current_module = module; - self.cx.recursion_count = depth + 1; + self.cx.current_expansion.depth = depth + 1; let (expansion, new_invocations) = self.collect_invocations(expansion); if expansions.len() == depth { @@ -207,7 +243,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } - let mut placeholder_expander = PlaceholderExpander::new(); + self.cx.current_expansion = orig_expansion_data; + + let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { let expansion = expansion.fold_with(&mut placeholder_expander); @@ -230,33 +268,31 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }, cx: self.cx, invocations: Vec::new(), + monotonic: self.monotonic, }; (expansion.fold_with(&mut collector), collector.invocations) }; - self.cx.cfg = crate_config; + + let mark = self.cx.current_expansion.mark; + self.cx.resolver.visit_expansion(mark, &result.0); result } - fn expand_invoc(&mut self, invoc: Invocation) -> Expansion { + fn expand_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { match invoc.kind { - InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc), - InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc), + InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext), + InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext), } } - fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion { + fn expand_attr_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { let Invocation { expansion_kind: kind, .. } = invoc; let (attr, item) = match invoc.kind { InvocationKind::Attr { attr, item } => (attr, item), _ => unreachable!(), }; - let extension = match self.cx.syntax_env.find(intern(&attr.name())) { - Some(extension) => extension, - None => unreachable!(), - }; - attr::mark_used(&attr); self.cx.bt_push(ExpnInfo { call_site: attr.span, @@ -267,7 +303,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } }); - match *extension { + match *ext { MultiModifier(ref mac) => { let item = mac.expand(self.cx, attr.span, &attr.node.value, item); kind.expect_from_annotatables(item) @@ -284,8 +320,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } /// Expand a macro invocation. Returns the result of expansion. - fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion { - let Invocation { mark, expansion_kind: kind, .. } = invoc; + fn expand_bang_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { + let (mark, kind) = (invoc.mark(), invoc.expansion_kind); let (attrs, mac, ident, span) = match invoc.kind { InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), _ => unreachable!(), @@ -306,19 +342,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } let extname = path.segments[0].identifier.name; - let extension = if let Some(extension) = self.cx.syntax_env.find(extname) { - extension - } else { - let mut err = - self.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname)); - self.cx.suggest_macro_name(&extname.as_str(), &mut err); - err.emit(); - return kind.dummy(span); - }; - let ident = ident.unwrap_or(keywords::Invalid.ident()); let marked_tts = mark_tts(&tts, mark); - let opt_expanded = match *extension { + let opt_expanded = match *ext { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { if ident.name != keywords::Invalid.name() { let msg = @@ -425,6 +451,7 @@ struct InvocationCollector<'a, 'b: 'a> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, invocations: Vec, + monotonic: bool, } macro_rules! fully_configure { @@ -442,10 +469,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.invocations.push(Invocation { kind: kind, expansion_kind: expansion_kind, - mark: mark, - module: self.cx.syntax_env.current_module, - backtrace: self.cx.backtrace, - depth: self.cx.recursion_count, + expansion_data: ExpansionData { mark: mark, ..self.cx.current_expansion.clone() }, }); placeholder(expansion_kind, mark.as_u32()) } @@ -462,50 +486,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } // If `item` is an attr invocation, remove and return the macro attribute. - fn classify_item(&self, mut item: T) -> (T, Option) { + fn classify_item(&mut self, mut item: T) -> (T, Option) { let mut attr = None; item = item.map_attrs(|mut attrs| { - for i in 0..attrs.len() { - if let Some(extension) = self.cx.syntax_env.find(intern(&attrs[i].name())) { - match *extension { - MultiModifier(..) | MultiDecorator(..) => { - attr = Some(attrs.remove(i)); - break; - } - _ => {} - } - } - } + attr = self.cx.resolver.find_attr_invoc(&mut attrs); attrs }); (item, attr) } - // does this attribute list contain "macro_use" ? - fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { - for attr in attrs { - let mut is_use = attr.check_name("macro_use"); - if attr.check_name("macro_escape") { - let msg = "macro_escape is a deprecated synonym for macro_use"; - let mut err = self.cx.struct_span_warn(attr.span, msg); - is_use = true; - if let ast::AttrStyle::Inner = attr.node.style { - err.help("consider an outer attribute, #[macro_use] mod ...").emit(); - } else { - err.emit(); - } - }; - - if is_use { - if !attr.is_word() { - self.cx.span_err(attr.span, "arguments to macro_use are not allowed here"); - } - return true; - } - } - false - } - fn configure(&mut self, node: T) -> Option { self.cfg.configure(node) } @@ -554,9 +543,14 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { None => return SmallVector::zero(), }; - let (mac, style, attrs) = match stmt.node { - StmtKind::Mac(mac) => mac.unwrap(), - _ => return noop_fold_stmt(stmt, self), + let (mac, style, attrs) = if let StmtKind::Mac(mac) = stmt.node { + mac.unwrap() + } else { + // The placeholder expander gives ids to statements, so we avoid folding the id here. + let ast::Stmt { id, node, span } = stmt; + return noop_fold_stmt_kind(node, self).into_iter().map(|node| { + ast::Stmt { id: id, node: node, span: span } + }).collect() }; let mut placeholder = @@ -574,11 +568,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } fn fold_block(&mut self, block: P) -> P { - let paths = self.cx.syntax_env.paths(); - let module = self.cx.syntax_env.add_module(false, true, paths); - let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module); + let orig_in_block = mem::replace(&mut self.cx.current_expansion.in_block, true); let result = noop_fold_block(block, self); - self.cx.syntax_env.current_module = orig_module; + self.cx.current_expansion.in_block = orig_in_block; result } @@ -613,8 +605,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { }) } ast::ItemKind::Mod(ast::Mod { inner, .. }) => { - let mut paths = (*self.cx.syntax_env.paths()).clone(); - paths.mod_path.push(item.ident); + if item.ident == keywords::Invalid.ident() { + return noop_fold_item(item, self); + } + + let mut module = (*self.cx.current_expansion.module).clone(); + module.mod_path.push(item.ident); // Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`). // In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`). @@ -622,29 +618,27 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP; if inline_module { - paths.directory.push(&*{ + module.directory.push(&*{ ::attr::first_attr_value_str_by_name(&item.attrs, "path") .unwrap_or(item.ident.name.as_str()) }); } else { - paths.directory = + module.directory = PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)); - paths.directory.pop(); + module.directory.pop(); } - let macro_use = self.contains_macro_use(&item.attrs); - let in_block = self.cx.syntax_env.in_block(); - let module = self.cx.syntax_env.add_module(macro_use, in_block, Rc::new(paths)); - let module = mem::replace(&mut self.cx.syntax_env.current_module, module); + let orig_module = + mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); let result = noop_fold_item(item, self); - self.cx.syntax_env.current_module = module; - result - }, + self.cx.current_expansion.module = orig_module; + return result; + } ast::ItemKind::ExternCrate(..) => { // We need to error on `#[macro_use] extern crate` when it isn't at the // crate root, because `$crate` won't work properly. - let is_crate_root = self.cx.syntax_env.is_crate_root(); - for def in self.cx.loader.load_crate(&*item, is_crate_root) { + let is_crate_root = self.cx.current_expansion.module.mod_path.len() == 1; + for def in self.cx.resolver.load_crate(&*item, is_crate_root) { match def { LoadedMacro::Def(def) => self.cx.insert_macro(def), LoadedMacro::CustomDerive(name, ext) => { @@ -652,7 +646,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } } } - SmallVector::one(item) + noop_fold_item(item, self) }, _ => noop_fold_item(item, self), } @@ -715,6 +709,15 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { noop_fold_item_kind(self.cfg.configure_item_kind(item), self) } + + fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId { + if self.monotonic { + assert_eq!(id, ast::DUMMY_NODE_ID); + self.cx.resolver.next_node_id() + } else { + id + } + } } pub struct ExpansionConfig<'feat> { @@ -766,7 +769,7 @@ pub fn expand_crate(cx: &mut ExtCtxt, user_exts: Vec, c: Crate) -> Crate { cx.initialize(user_exts, &c); - cx.expander().expand_crate(c) + cx.monotonic_expander().expand_crate(c) } // Expands crate using supplied MacroExpander - allows for @@ -803,110 +806,3 @@ impl Folder for Marker { fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec { noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None}) } - - -#[cfg(test)] -mod tests { - use super::{expand_crate, ExpansionConfig}; - use ast; - use ext::base::{ExtCtxt, DummyMacroLoader}; - use parse; - use util::parser_testing::{string_to_parser}; - use visit; - use visit::Visitor; - - // a visitor that extracts the paths - // from a given thingy and puts them in a mutable - // array (passed in to the traversal) - #[derive(Clone)] - struct PathExprFinderContext { - path_accumulator: Vec , - } - - impl Visitor for PathExprFinderContext { - fn visit_expr(&mut self, expr: &ast::Expr) { - if let ast::ExprKind::Path(None, ref p) = expr.node { - self.path_accumulator.push(p.clone()); - } - visit::walk_expr(self, expr); - } - } - - // these following tests are quite fragile, in that they don't test what - // *kind* of failure occurs. - - fn test_ecfg() -> ExpansionConfig<'static> { - ExpansionConfig::default("test".to_string()) - } - - // make sure that macros can't escape fns - #[should_panic] - #[test] fn macros_cant_escape_fns_test () { - let src = "fn bogus() {macro_rules! z (() => (3+4));}\ - fn inty() -> i32 { z!() }".to_string(); - let sess = parse::ParseSess::new(); - let crate_ast = parse::parse_crate_from_source_str( - "".to_string(), - src, - Vec::new(), &sess).unwrap(); - // should fail: - let mut loader = DummyMacroLoader; - let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast); - } - - // make sure that macros can't escape modules - #[should_panic] - #[test] fn macros_cant_escape_mods_test () { - let src = "mod foo {macro_rules! z (() => (3+4));}\ - fn inty() -> i32 { z!() }".to_string(); - let sess = parse::ParseSess::new(); - let crate_ast = parse::parse_crate_from_source_str( - "".to_string(), - src, - Vec::new(), &sess).unwrap(); - let mut loader = DummyMacroLoader; - let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast); - } - - // macro_use modules should allow macros to escape - #[test] fn macros_can_escape_flattened_mods_test () { - let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\ - fn inty() -> i32 { z!() }".to_string(); - let sess = parse::ParseSess::new(); - let crate_ast = parse::parse_crate_from_source_str( - "".to_string(), - src, - Vec::new(), &sess).unwrap(); - let mut loader = DummyMacroLoader; - let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast); - } - - fn expand_crate_str(crate_str: String) -> ast::Crate { - let ps = parse::ParseSess::new(); - let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod()); - // the cfg argument actually does matter, here... - let mut loader = DummyMacroLoader; - let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader); - expand_crate(&mut ecx, vec![], crate_ast) - } - - #[test] fn macro_tokens_should_match(){ - expand_crate_str( - "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string()); - } - - // should be able to use a bound identifier as a literal in a macro definition: - #[test] fn self_macro_parsing(){ - expand_crate_str( - "macro_rules! foo ((zz) => (287;)); - fn f(zz: i32) {foo!(zz);}".to_string() - ); - } - - // create a really evil test case where a $x appears inside a binding of $x - // but *shouldn't* bind because it was inserted by a different macro.... - // can't write this test case until we have macro-generating macros. -} diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs index 27e8eab62e114..34126fac4ac78 100644 --- a/src/libsyntax/ext/hygiene.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -29,7 +29,7 @@ pub struct SyntaxContextData { pub prev_ctxt: SyntaxContext, } -/// A mark represents a unique id associated with a macro expansion. +/// A mark is a unique id associated with a macro expansion. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct Mark(u32); @@ -41,6 +41,11 @@ impl Mark { }) } + /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST. + pub fn root() -> Self { + Mark(0) + } + pub fn as_u32(&self) -> u32 { self.0 } @@ -56,8 +61,8 @@ impl HygieneData { fn new() -> Self { HygieneData { syntax_contexts: vec![SyntaxContextData { - outer_mark: Mark(0), // the null mark - prev_ctxt: SyntaxContext(0), // the empty context + outer_mark: Mark::root(), + prev_ctxt: SyntaxContext::empty(), }], markings: HashMap::new(), next_mark: Mark(1), diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index abadcf867b146..47f366a88768e 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -10,13 +10,16 @@ use ast; use codemap::{DUMMY_SP, dummy_spanned}; +use ext::base::ExtCtxt; use ext::expand::{Expansion, ExpansionKind}; use fold::*; use parse::token::keywords; use ptr::P; +use util::move_map::MoveMap; use util::small_vector::SmallVector; use std::collections::HashMap; +use std::mem; pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion { fn mac_placeholder() -> ast::Mac { @@ -69,14 +72,18 @@ pub fn macro_scope_placeholder() -> Expansion { placeholder(ExpansionKind::Items, ast::DUMMY_NODE_ID) } -pub struct PlaceholderExpander { +pub struct PlaceholderExpander<'a, 'b: 'a> { expansions: HashMap, + cx: &'a mut ExtCtxt<'b>, + monotonic: bool, } -impl PlaceholderExpander { - pub fn new() -> Self { +impl<'a, 'b> PlaceholderExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { PlaceholderExpander { + cx: cx, expansions: HashMap::new(), + monotonic: monotonic, } } @@ -89,7 +96,7 @@ impl PlaceholderExpander { } } -impl Folder for PlaceholderExpander { +impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { fn fold_item(&mut self, item: P) -> SmallVector> { match item.node { // Scope placeholder @@ -155,6 +162,56 @@ impl Folder for PlaceholderExpander { _ => noop_fold_ty(ty, self), } } + + fn fold_block(&mut self, block: P) -> P { + noop_fold_block(block, self).map(|mut block| { + let mut macros = Vec::new(); + let mut remaining_stmts = block.stmts.len(); + + block.stmts = block.stmts.move_flat_map(|mut stmt| { + remaining_stmts -= 1; + + // Scope placeholder + if let ast::StmtKind::Item(ref item) = stmt.node { + if let ast::ItemKind::Mac(..) = item.node { + macros.push(item.ident.ctxt.data().outer_mark); + return None; + } + } + + match stmt.node { + // Avoid wasting a node id on a trailing expression statement, + // which shares a HIR node with the expression itself. + ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id, + + _ if self.monotonic => { + assert_eq!(stmt.id, ast::DUMMY_NODE_ID); + stmt.id = self.cx.resolver.next_node_id(); + } + + _ => {} + } + + if self.monotonic && !macros.is_empty() { + let macros = mem::replace(&mut macros, Vec::new()); + self.cx.resolver.add_expansions_at_stmt(stmt.id, macros); + } + + Some(stmt) + }); + + block + }) + } + + fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod { + let mut module = noop_fold_mod(module, self); + module.items = module.items.move_flat_map(|item| match item.node { + ast::ItemKind::Mac(_) => None, // remove scope placeholders from modules + _ => Some(item), + }); + module + } } pub fn reconstructed_macro_rules(def: &ast::MacroDef, path: &ast::Path) -> Expansion { diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 105b226111738..e75e41d0c2d4b 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -74,8 +74,8 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTre pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) -> Box { base::check_zero_tts(cx, sp, tts, "module_path!"); - let paths = cx.syntax_env.paths(); - let string = paths.mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); + let mod_path = &cx.current_expansion.module.mod_path; + let string = mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); base::MacEager::expr(cx.expr_str( sp, diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index ed80ec9cbc49e..51ef45b97be6f 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -211,8 +211,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, imported_from, rhs); let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr)); - p.directory = cx.syntax_env.paths().directory.clone(); - p.restrictions = match cx.syntax_env.in_block() { + p.directory = cx.current_expansion.module.directory.clone(); + p.restrictions = match cx.current_expansion.in_block { true => Restrictions::NO_NONINLINE_MOD, false => Restrictions::empty(), }; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 7500bfe9caa80..9fb4d0203f41e 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1320,51 +1320,27 @@ pub fn noop_fold_exprs(es: Vec>, folder: &mut T) -> Vec(Stmt {node, span, id}: Stmt, folder: &mut T) - -> SmallVector { +pub fn noop_fold_stmt(Stmt {node, span, id}: Stmt, folder: &mut T) -> SmallVector { let id = folder.new_id(id); let span = folder.new_span(span); + noop_fold_stmt_kind(node, folder).into_iter().map(|node| { + Stmt { id: id, node: node, span: span } + }).collect() +} +pub fn noop_fold_stmt_kind(node: StmtKind, folder: &mut T) -> SmallVector { match node { - StmtKind::Local(local) => SmallVector::one(Stmt { - id: id, - node: StmtKind::Local(folder.fold_local(local)), - span: span, - }), - StmtKind::Item(item) => folder.fold_item(item).into_iter().map(|item| Stmt { - id: id, - node: StmtKind::Item(item), - span: span, - }).collect(), + StmtKind::Local(local) => SmallVector::one(StmtKind::Local(folder.fold_local(local))), + StmtKind::Item(item) => folder.fold_item(item).into_iter().map(StmtKind::Item).collect(), StmtKind::Expr(expr) => { - if let Some(expr) = folder.fold_opt_expr(expr) { - SmallVector::one(Stmt { - id: id, - node: StmtKind::Expr(expr), - span: span, - }) - } else { - SmallVector::zero() - } + folder.fold_opt_expr(expr).into_iter().map(StmtKind::Expr).collect() } StmtKind::Semi(expr) => { - if let Some(expr) = folder.fold_opt_expr(expr) { - SmallVector::one(Stmt { - id: id, - node: StmtKind::Semi(expr), - span: span, - }) - } else { - SmallVector::zero() - } + folder.fold_opt_expr(expr).into_iter().map(StmtKind::Semi).collect() } - StmtKind::Mac(mac) => SmallVector::one(Stmt { - id: id, - node: StmtKind::Mac(mac.map(|(mac, semi, attrs)| { - (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into()) - })), - span: span, - }) + StmtKind::Mac(mac) => SmallVector::one(StmtKind::Mac(mac.map(|(mac, semi, attrs)| { + (folder.fold_mac(mac), semi, fold_attrs(attrs.into(), folder).into()) + }))), } } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3108296e778a2..6327e8f71bcd5 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -28,7 +28,7 @@ use errors; use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; -use ext::base::{ExtCtxt, DummyMacroLoader}; +use ext::base::{ExtCtxt, Resolver}; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; use fold::Folder; @@ -70,6 +70,7 @@ struct TestCtxt<'a> { // Traverse the crate, collecting all the test functions, eliding any // existing main functions, and synthesizing a main test harness pub fn modify_for_testing(sess: &ParseSess, + resolver: &mut Resolver, should_test: bool, krate: ast::Crate, span_diagnostic: &errors::Handler) -> ast::Crate { @@ -82,7 +83,7 @@ pub fn modify_for_testing(sess: &ParseSess, "reexport_test_harness_main"); if should_test { - generate_test_harness(sess, reexport_test_harness_main, krate, span_diagnostic) + generate_test_harness(sess, resolver, reexport_test_harness_main, krate, span_diagnostic) } else { krate } @@ -248,27 +249,28 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, }).chain(tested_submods.into_iter().map(|(r, sym)| { let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]); cx.ext_cx.item_use_simple_(DUMMY_SP, ast::Visibility::Public, r, path) - })); + })).collect(); let reexport_mod = ast::Mod { inner: DUMMY_SP, - items: items.collect(), + items: items, }; let sym = token::gensym_ident("__test_reexports"); - let it = P(ast::Item { + let it = cx.ext_cx.monotonic_expander().fold_item(P(ast::Item { ident: sym.clone(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, node: ast::ItemKind::Mod(reexport_mod), vis: ast::Visibility::Public, span: DUMMY_SP, - }); + })).pop().unwrap(); (it, sym) } fn generate_test_harness(sess: &ParseSess, + resolver: &mut Resolver, reexport_test_harness_main: Option, krate: ast::Crate, sd: &errors::Handler) -> ast::Crate { @@ -276,13 +278,10 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mut loader = DummyMacroLoader; let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, - ext_cx: ExtCtxt::new(sess, vec![], - ExpansionConfig::default("test".to_string()), - &mut loader), + ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), resolver), path: Vec::new(), testfns: Vec::new(), reexport_test_harness_main: reexport_test_harness_main, @@ -511,16 +510,17 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { items: vec![import, mainfn, tests], }; let item_ = ast::ItemKind::Mod(testmod); - let mod_ident = token::gensym_ident("__test"); - let item = P(ast::Item { + + let mut expander = cx.ext_cx.monotonic_expander(); + let item = expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: mod_ident, attrs: vec![], node: item_, vis: ast::Visibility::Public, span: DUMMY_SP, - }); + })).pop().unwrap(); let reexport = cx.reexport_test_harness_main.as_ref().map(|s| { // building `use = __test::main` let reexport_ident = token::str_to_ident(&s); @@ -529,14 +529,14 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { nospan(ast::ViewPathSimple(reexport_ident, path_node(vec![mod_ident, token::str_to_ident("main")]))); - P(ast::Item { + expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: keywords::Invalid.ident(), attrs: vec![], node: ast::ItemKind::Use(P(use_path)), vis: ast::Visibility::Inherited, span: DUMMY_SP - }) + })).pop().unwrap() }); debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item)); diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index c7afaaf4796a4..d7bc2a6faeeb9 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -11,20 +11,14 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData}; +use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData}; use syntax::attr; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; -use syntax::parse::token::InternedString; +use syntax::parse::token::{keywords, InternedString}; use syntax::ptr::P; use syntax_pos::Span; -#[derive(PartialEq)] -enum Mode { - Deep, - Shallow, -} - pub fn expand_deriving_clone(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, @@ -40,29 +34,38 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, // if we used the short form with generics, we'd have to bound the generics with // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something // that is Clone but not Copy. and until specialization we can't write both impls. + // - the item is a union with Copy fields + // Unions with generic parameters still can derive Clone because they require Copy + // for deriving, Clone alone is not enough. + // Whever Clone is implemented for fields is irrelevant so we don't assert it. let bounds; - let unify_fieldless_variants; let substructure; + let is_shallow; match *item { Annotatable::Item(ref annitem) => { match annitem.node { ItemKind::Struct(_, Generics { ref ty_params, .. }) | ItemKind::Enum(_, Generics { ref ty_params, .. }) - if ty_params.is_empty() && - attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => { - + if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") && + ty_params.is_empty() => { + bounds = vec![]; + is_shallow = true; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone_shallow("Clone", c, s, sub, false) + })); + } + ItemKind::Union(..) => { bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; - unify_fieldless_variants = true; + is_shallow = true; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Shallow) + cs_clone_shallow("Clone", c, s, sub, true) })); } - _ => { bounds = vec![]; - unify_fieldless_variants = false; + is_shallow = false; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Deep) + cs_clone("Clone", c, s, sub) })); } } @@ -80,7 +83,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, additional_bounds: bounds, generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "clone", generics: LifetimeBounds::empty(), @@ -89,37 +92,72 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, ret_ty: Self_, attributes: attrs, is_unsafe: false, - unify_fieldless_variants: unify_fieldless_variants, + unify_fieldless_variants: false, combine_substructure: substructure, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, is_shallow) +} + +fn cs_clone_shallow(name: &str, + cx: &mut ExtCtxt, + trait_span: Span, + substr: &Substructure, + is_union: bool) + -> P { + fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec, + ty: P, span: Span, helper_name: &str) { + // Generate statement `let _: helper_name;`, + // set the expn ID so we can use the unstable struct. + let span = super::allow_unstable(cx, span, "derive(Clone)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["clone", helper_name]), + vec![], vec![ty], vec![]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); + } + fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &VariantData) { + for field in variant.fields() { + // let _: AssertParamIsClone; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone"); + } + } + + let mut stmts = Vec::new(); + if is_union { + // let _: AssertParamIsCopy; + let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfType.ident())); + assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); + } else { + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(cx, &mut stmts, vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(cx, &mut stmts, &variant.node.data); + } + } + _ => cx.span_bug(trait_span, &format!("unexpected substructure in \ + shallow `derive({})`", name)) + } + } + stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); + cx.expr_block(cx.block(trait_span, stmts)) } fn cs_clone(name: &str, cx: &mut ExtCtxt, trait_span: Span, - substr: &Substructure, - mode: Mode) + substr: &Substructure) -> P { let ctor_path; let all_fields; - let fn_path = match mode { - Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]), - Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]), - }; + let fn_path = cx.std_path(&["clone", "Clone", "clone"]); let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; - - let span = if mode == Mode::Shallow { - // set the expn ID so we can call the unstable method - super::allow_unstable(cx, field.span, "derive(Clone)") - } else { - field.span - }; - cx.expr_call_global(span, fn_path.clone(), args) + cx.expr_call_global(field.span, fn_path.clone(), args) }; let vdata; @@ -145,43 +183,31 @@ fn cs_clone(name: &str, } } - match mode { - Mode::Shallow => { - let mut stmts = all_fields.iter().map(|f| { - let call = subcall(cx, f); - cx.stmt_expr(call) - }).collect::>(); - stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); - cx.expr_block(cx.block(trait_span, stmts)) - } - Mode::Deep => { - match *vdata { - VariantData::Struct(..) => { - let fields = all_fields.iter() - .map(|field| { - let ident = match field.name { - Some(i) => i, - None => { - cx.span_bug(trait_span, - &format!("unnamed field in normal struct in \ - `derive({})`", - name)) - } - }; - let call = subcall(cx, field); - cx.field_imm(field.span, ident, call) - }) - .collect::>(); + match *vdata { + VariantData::Struct(..) => { + let fields = all_fields.iter() + .map(|field| { + let ident = match field.name { + Some(i) => i, + None => { + cx.span_bug(trait_span, + &format!("unnamed field in normal struct in \ + `derive({})`", + name)) + } + }; + let call = subcall(cx, field); + cx.field_imm(field.span, ident, call) + }) + .collect::>(); - cx.expr_struct(trait_span, ctor_path, fields) - } - VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); - let path = cx.expr_path(ctor_path); - cx.expr_call(trait_span, path, subcalls) - } - VariantData::Unit(..) => cx.expr_path(ctor_path), - } + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) } + VariantData::Unit(..) => cx.expr_path(ctor_path), } } diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs index 425a47a991bc4..fa0fb2492c551 100644 --- a/src/libsyntax_ext/deriving/cmp/eq.rs +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -11,7 +11,7 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, MetaItem}; +use syntax::ast::{self, Expr, MetaItem}; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::parse::token::InternedString; @@ -23,22 +23,6 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { - fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P { - cs_same_method(|cx, span, exprs| { - // create `a.(); b.(); c.(); ...` - // (where method is `assert_receiver_is_total_eq`) - let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect(); - let block = cx.block(span, stmts); - cx.expr_block(block) - }, - Box::new(|cx, sp, _, _| { - cx.span_bug(sp, "non matching enums in derive(Eq)?") - }), - cx, - span, - substr) - } - let inline = cx.meta_word(span, InternedString::new("inline")); let hidden = cx.meta_list_item_word(span, InternedString::new("hidden")); let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]); @@ -50,7 +34,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, additional_bounds: Vec::new(), generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "assert_receiver_is_total_eq", generics: LifetimeBounds::empty(), @@ -66,5 +50,38 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, true) +} + +fn cs_total_eq_assert(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { + fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec, + ty: P, span: Span, helper_name: &str) { + // Generate statement `let _: helper_name;`, + // set the expn ID so we can use the unstable struct. + let span = super::allow_unstable(cx, span, "derive(Eq)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["cmp", helper_name]), + vec![], vec![ty], vec![]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); + } + fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &ast::VariantData) { + for field in variant.fields() { + // let _: AssertParamIsEq; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq"); + } + } + + let mut stmts = Vec::new(); + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(cx, &mut stmts, vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(cx, &mut stmts, &variant.node.data); + } + } + _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`") + } + cx.expr_block(cx.block(trait_span, stmts)) } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 600f5d335c472..339a6c477ccd5 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> { mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut FnMut(Annotatable)) { + self.expand_ext(cx, mitem, item, push, false); + } + + pub fn expand_ext(&self, + cx: &mut ExtCtxt, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut FnMut(Annotatable), + from_scratch: bool) { match *item { Annotatable::Item(ref item) => { let newitem = match item.node { ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch) } ast::ItemKind::Enum(ref enum_def, ref generics) => { - self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics) + self.expand_enum_def(cx, enum_def, &item.attrs, + item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, + generics, from_scratch) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt, struct_def: &'a VariantData, type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P { let field_tys: Vec> = struct_def.fields() .iter() @@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_struct_method_body(cx, self, struct_def, @@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> { enum_def: &'a EnumDef, type_attrs: &[ast::Attribute], type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P { let mut field_tys = Vec::new(); @@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_enum_method_body(cx, self, enum_def, diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index fcbce36389082..6162beb80eccc 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -11,8 +11,7 @@ //! The compiler code necessary to implement the `#[derive]` extensions. use syntax::ast::{self, MetaItem}; -use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv}; -use syntax::ext::base::MultiModifier; +use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::feature_gate; use syntax::codemap; @@ -89,7 +88,7 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { } } -fn expand_derive(cx: &mut ExtCtxt, +pub fn expand_derive(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, annotatable: Annotatable) @@ -243,10 +242,6 @@ fn expand_derive(cx: &mut ExtCtxt, macro_rules! derive_traits { ($( $name:expr => $func:path, )+) => { - pub fn register_all(env: &mut SyntaxEnv) { - env.insert(intern("derive"), MultiModifier(Box::new(expand_derive))); - } - pub fn is_builtin_trait(name: &str) -> bool { match name { $( $name )|+ => true, diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 06b16095d1963..892ebcfa76129 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -17,7 +17,6 @@ use syntax::ast; use syntax::ext::base::*; use syntax::ext::base; use syntax::ext::build::AstBuilder; -use syntax::fold::Folder; use syntax::parse::token::{self, keywords}; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; @@ -702,10 +701,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let macsp = ecx.call_site(); - // Expand the format literal so that efmt.span will have a backtrace. This - // is essential for locating a bug when the format literal is generated in - // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")). - let efmt = ecx.expander().fold_expr(efmt); + let msg = "format argument must be a string literal."; + let fmt = match expr_to_spanned_string(ecx, efmt, msg) { + Some(fmt) => fmt, + None => return DummyResult::raw_expr(sp), + }; + let mut cx = Context { ecx: ecx, args: args, @@ -723,14 +724,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, str_pieces: Vec::new(), all_pieces_simple: true, macsp: macsp, - fmtsp: efmt.span, - }; - let fmt = match expr_to_string(cx.ecx, efmt, "format argument must be a string literal.") { - Some((fmt, _)) => fmt, - None => return DummyResult::raw_expr(sp), + fmtsp: fmt.span, }; - let mut parser = parse::Parser::new(&fmt); + let mut parser = parse::Parser::new(&fmt.node.0); let mut pieces = vec![]; loop { diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 2065d92fd6ed7..3a6212e5445ce 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -34,11 +34,6 @@ extern crate syntax_pos; extern crate rustc_macro; extern crate rustc_errors as errors; -use syntax::ext::base::{MacroExpanderFn, NormalTT}; -use syntax::ext::base::{SyntaxEnv, SyntaxExtension}; -use syntax::parse::token::intern; - - mod asm; mod cfg; mod concat; @@ -53,28 +48,67 @@ pub mod rustc_macro_registrar; // for custom_derive pub mod deriving; -pub fn register_builtins(env: &mut SyntaxEnv) { - // utility function to simplify creating NormalTT syntax extensions - fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { - NormalTT(Box::new(f), None, false) +use std::rc::Rc; +use syntax::ast; +use syntax::ext::base::{MacroExpanderFn, MacroRulesTT, NormalTT, MultiModifier}; +use syntax::ext::hygiene::Mark; +use syntax::parse::token::intern; + +pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quotes: bool) { + let mut register = |name, ext| { + resolver.add_macro(Mark::root(), ast::Ident::with_empty_ctxt(intern(name)), Rc::new(ext)); + }; + + register("macro_rules", MacroRulesTT); + + macro_rules! register { + ($( $name:ident: $f:expr, )*) => { $( + register(stringify!($name), NormalTT(Box::new($f as MacroExpanderFn), None, false)); + )* } } - env.insert(intern("asm"), builtin_normal_expander(asm::expand_asm)); - env.insert(intern("cfg"), builtin_normal_expander(cfg::expand_cfg)); - env.insert(intern("concat"), - builtin_normal_expander(concat::expand_syntax_ext)); - env.insert(intern("concat_idents"), - builtin_normal_expander(concat_idents::expand_syntax_ext)); - env.insert(intern("env"), builtin_normal_expander(env::expand_env)); - env.insert(intern("option_env"), - builtin_normal_expander(env::expand_option_env)); - env.insert(intern("format_args"), - // format_args uses `unstable` things internally. - NormalTT(Box::new(format::expand_format_args), None, true)); - env.insert(intern("log_syntax"), - builtin_normal_expander(log_syntax::expand_syntax_ext)); - env.insert(intern("trace_macros"), - builtin_normal_expander(trace_macros::expand_trace_macros)); - - deriving::register_all(env); + if enable_quotes { + use syntax::ext::quote::*; + register! { + quote_tokens: expand_quote_tokens, + quote_expr: expand_quote_expr, + quote_ty: expand_quote_ty, + quote_item: expand_quote_item, + quote_pat: expand_quote_pat, + quote_arm: expand_quote_arm, + quote_stmt: expand_quote_stmt, + quote_matcher: expand_quote_matcher, + quote_attr: expand_quote_attr, + quote_arg: expand_quote_arg, + quote_block: expand_quote_block, + quote_meta_item: expand_quote_meta_item, + quote_path: expand_quote_path, + } + } + + use syntax::ext::source_util::*; + register! { + line: expand_line, + column: expand_column, + file: expand_file, + stringify: expand_stringify, + include: expand_include, + include_str: expand_include_str, + include_bytes: expand_include_bytes, + module_path: expand_mod, + + asm: asm::expand_asm, + cfg: cfg::expand_cfg, + concat: concat::expand_syntax_ext, + concat_idents: concat_idents::expand_syntax_ext, + env: env::expand_env, + option_env: env::expand_option_env, + log_syntax: log_syntax::expand_syntax_ext, + trace_macros: trace_macros::expand_trace_macros, + } + + // format_args uses `unstable` things internally. + register("format_args", NormalTT(Box::new(format::expand_format_args), None, true)); + + register("derive", MultiModifier(Box::new(deriving::expand_derive))); } diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index 7693e2416f4b0..ce3e53cdf97f4 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -13,12 +13,13 @@ use std::mem; use errors; use syntax::ast::{self, Ident, NodeId}; use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; -use syntax::ext::base::{ExtCtxt, DummyMacroLoader}; +use syntax::ext::base::ExtCtxt; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; use syntax::parse::ParseSess; use syntax::parse::token::{self, InternedString}; use syntax::feature_gate::Features; +use syntax::fold::Folder; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; use syntax::visit::{self, Visitor}; @@ -39,16 +40,14 @@ struct CollectCustomDerives<'a> { } pub fn modify(sess: &ParseSess, + resolver: &mut ::syntax::ext::base::Resolver, mut krate: ast::Crate, is_rustc_macro_crate: bool, num_crate_types: usize, handler: &errors::Handler, features: &Features) -> ast::Crate { - let mut loader = DummyMacroLoader; - let mut cx = ExtCtxt::new(sess, - Vec::new(), - ExpansionConfig::default("rustc_macro".to_string()), - &mut loader); + let ecfg = ExpansionConfig::default("rustc_macro".to_string()); + let mut cx = ExtCtxt::new(sess, Vec::new(), ecfg, resolver); let mut collect = CollectCustomDerives { derives: Vec::new(), @@ -268,13 +267,11 @@ fn mk_registrar(cx: &mut ExtCtxt, i.vis = ast::Visibility::Public; i }); - let module = cx.item_mod(span, - span, - ast::Ident::with_empty_ctxt(token::gensym("registrar")), - Vec::new(), - vec![krate, func]); - module.map(|mut i| { + let ident = ast::Ident::with_empty_ctxt(token::gensym("registrar")); + let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| { i.vis = ast::Visibility::Public; i - }) + }); + + cx.monotonic_expander().fold_item(module).pop().unwrap() } diff --git a/src/test/compile-fail-fulldeps/qquote.rs b/src/test/compile-fail-fulldeps/qquote.rs index e29ded8a052c6..3e5d17e2ffb17 100644 --- a/src/test/compile-fail-fulldeps/qquote.rs +++ b/src/test/compile-fail-fulldeps/qquote.rs @@ -22,11 +22,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/compile-fail/E0407.rs b/src/test/compile-fail/E0407.rs index 2a150b7451210..c207dbfca5565 100644 --- a/src/test/compile-fail/E0407.rs +++ b/src/test/compile-fail/E0407.rs @@ -18,7 +18,7 @@ impl Foo for Bar { fn a() {} fn b() {} //~^ ERROR E0407 - //~| NOTE not a member of `Foo` + //~| NOTE not a member of trait `Foo` } fn main() { diff --git a/src/test/compile-fail/E0438.rs b/src/test/compile-fail/E0438.rs index f549d62aebfea..2e2df4bee5a35 100644 --- a/src/test/compile-fail/E0438.rs +++ b/src/test/compile-fail/E0438.rs @@ -10,11 +10,11 @@ #![feature(associated_consts)] -trait Foo {} +trait Bar {} -impl Foo for i32 { +impl Bar for i32 { const BAR: bool = true; //~ ERROR E0438 - //~| NOTE not a member of trait `Foo` + //~| NOTE not a member of trait `Bar` } fn main () { diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs index 19975d79b60be..8b6b8d9ecb08e 100644 --- a/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs +++ b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs @@ -18,16 +18,12 @@ struct S { b: u16, } +#[derive(Clone, Copy)] union U { s: S, c: u32, } -impl Clone for U { - fn clone(&self) -> Self { *self } -} -impl Copy for U {} - fn main() { unsafe { { diff --git a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs index e8989a3c2d499..ecc698acc317f 100644 --- a/src/test/compile-fail/borrowck/borrowck-union-borrow.rs +++ b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs @@ -12,16 +12,12 @@ #![feature(untagged_unions)] +#[derive(Clone, Copy)] union U { a: u8, b: u64, } -impl Clone for U { - fn clone(&self) -> Self { *self } -} -impl Copy for U {} - fn main() { unsafe { let mut u = U { b: 0 }; diff --git a/src/test/compile-fail/issue-36053-2.rs b/src/test/compile-fail/issue-36053-2.rs new file mode 100644 index 0000000000000..7da529487aa86 --- /dev/null +++ b/src/test/compile-fail/issue-36053-2.rs @@ -0,0 +1,21 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #36053. ICE was caused due to obligations +// being added to a special, dedicated fulfillment cx during +// a probe. + +use std::iter::once; +fn main() { + once::<&str>("str").fuse().filter(|a: &str| true).count(); + //~^ ERROR no method named `count` + //~| ERROR E0281 + //~| ERROR E0281 +} diff --git a/src/test/compile-fail/macro-expansion-tests.rs b/src/test/compile-fail/macro-expansion-tests.rs new file mode 100644 index 0000000000000..a064e69bc6d59 --- /dev/null +++ b/src/test/compile-fail/macro-expansion-tests.rs @@ -0,0 +1,46 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod macros_cant_escape_fns { + fn f() { + macro_rules! m { () => { 3 + 4 } } + } + fn g() -> i32 { m!() } //~ ERROR macro undefined +} + +mod macros_cant_escape_mods { + mod f { + macro_rules! m { () => { 3 + 4 } } + } + fn g() -> i32 { m!() } //~ ERROR macro undefined +} + +mod macros_can_escape_flattened_mods_test { + #[macro_use] + mod f { + macro_rules! m { () => { 3 + 4 } } + } + fn g() -> i32 { m!() } +} + +fn macro_tokens_should_match() { + macro_rules! m { (a) => { 13 } } + m!(a); +} + +// should be able to use a bound identifier as a literal in a macro definition: +fn self_macro_parsing() { + macro_rules! foo { (zz) => { 287; } } + fn f(zz: i32) { + foo!(zz); + } +} + +fn main() {} diff --git a/src/test/compile-fail/union/union-copy.rs b/src/test/compile-fail/union/union-copy.rs index 6e08ae0074d48..9014b3f2956b7 100644 --- a/src/test/compile-fail/union/union-copy.rs +++ b/src/test/compile-fail/union/union-copy.rs @@ -10,16 +10,16 @@ #![feature(untagged_unions)] +#[derive(Clone)] union U { a: u8 } +#[derive(Clone)] union W { a: String } -impl Clone for U { fn clone(&self) { panic!(); } } -impl Clone for W { fn clone(&self) { panic!(); } } impl Copy for U {} // OK impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type diff --git a/src/test/compile-fail/union/union-derive-clone.rs b/src/test/compile-fail/union/union-derive-clone.rs new file mode 100644 index 0000000000000..6e226d7d79f9f --- /dev/null +++ b/src/test/compile-fail/union/union-derive-clone.rs @@ -0,0 +1,41 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +#[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied +union U1 { + a: u8, +} + +#[derive(Clone)] +union U2 { + a: u8, // OK +} + +impl Copy for U2 {} + +#[derive(Clone, Copy)] +union U3 { + a: u8, // OK +} + +#[derive(Clone, Copy)] +union U4 { + a: T, // OK +} + +#[derive(Clone)] +struct CloneNoCopy; + +fn main() { + let u = U4 { a: CloneNoCopy }; + let w = u.clone(); //~ ERROR no method named `clone` found for type `U4` +} diff --git a/src/test/compile-fail/union/union-derive-eq.rs b/src/test/compile-fail/union/union-derive-eq.rs new file mode 100644 index 0000000000000..9dfec288c1572 --- /dev/null +++ b/src/test/compile-fail/union/union-derive-eq.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +#[derive(Eq)] // OK +union U1 { + a: u8, +} + +impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive(PartialEq)] +struct PartialEqNotEq; + +#[derive(Eq)] +union U2 { + a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied +} + +impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } } + +fn main() {} diff --git a/src/test/compile-fail/union/union-derive.rs b/src/test/compile-fail/union/union-derive.rs index 0f78e96f640c7..26dbdfd0b4118 100644 --- a/src/test/compile-fail/union/union-derive.rs +++ b/src/test/compile-fail/union/union-derive.rs @@ -13,9 +13,7 @@ #![feature(untagged_unions)] #[derive( - Clone, //~ ERROR this trait cannot be derived for unions PartialEq, //~ ERROR this trait cannot be derived for unions - Eq, //~ ERROR this trait cannot be derived for unions PartialOrd, //~ ERROR this trait cannot be derived for unions Ord, //~ ERROR this trait cannot be derived for unions Hash, //~ ERROR this trait cannot be derived for unions diff --git a/src/test/run-fail-fulldeps/qquote.rs b/src/test/run-fail-fulldeps/qquote.rs index 47e97abbbaa47..1458583ff5830 100644 --- a/src/test/run-fail-fulldeps/qquote.rs +++ b/src/test/run-fail-fulldeps/qquote.rs @@ -25,11 +25,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/run-fail/bounds-check-no-overflow.rs b/src/test/run-fail/bounds-check-no-overflow.rs index 4d502cb2106b1..3d1cbb446e848 100644 --- a/src/test/run-fail/bounds-check-no-overflow.rs +++ b/src/test/run-fail/bounds-check-no-overflow.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:assertion failed: index < self.len() +// error-pattern:index out of bounds use std::usize; use std::mem::size_of; diff --git a/src/test/run-pass-fulldeps/qquote.rs b/src/test/run-pass-fulldeps/qquote.rs index a4f0e35cc5ac7..2a53a62a5ab60 100644 --- a/src/test/run-pass-fulldeps/qquote.rs +++ b/src/test/run-pass-fulldeps/qquote.rs @@ -21,11 +21,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs index 42c0da6286bdc..10e315f269f97 100644 --- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs +++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux1.rs @@ -22,6 +22,8 @@ pub type F = Option; pub type G = usize; pub type H = &'static str; pub type I = Box; +pub type I32Iterator = Iterator; +pub type U32Iterator = Iterator; pub fn id_A() -> TypeId { TypeId::of::() } pub fn id_B() -> TypeId { TypeId::of::() } @@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::() } pub fn id_I() -> TypeId { TypeId::of::() } pub fn foo() -> TypeId { TypeId::of::() } + +pub fn id_i32_iterator() -> TypeId { TypeId::of::() } +pub fn id_u32_iterator() -> TypeId { TypeId::of::() } diff --git a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs index 42c0da6286bdc..10e315f269f97 100644 --- a/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs +++ b/src/test/run-pass/auxiliary/typeid-intrinsic-aux2.rs @@ -22,6 +22,8 @@ pub type F = Option; pub type G = usize; pub type H = &'static str; pub type I = Box; +pub type I32Iterator = Iterator; +pub type U32Iterator = Iterator; pub fn id_A() -> TypeId { TypeId::of::() } pub fn id_B() -> TypeId { TypeId::of::() } @@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::() } pub fn id_I() -> TypeId { TypeId::of::() } pub fn foo() -> TypeId { TypeId::of::() } + +pub fn id_i32_iterator() -> TypeId { TypeId::of::() } +pub fn id_u32_iterator() -> TypeId { TypeId::of::() } diff --git a/src/test/run-pass/issue-35546.rs b/src/test/run-pass/issue-35546.rs new file mode 100644 index 0000000000000..e8d14f1d42146 --- /dev/null +++ b/src/test/run-pass/issue-35546.rs @@ -0,0 +1,28 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #35546. Check that we are able to codegen +// this. Before we had problems because of the drop glue signature +// around dropping a trait object (specifically, when dropping the +// `value` field of `Node`). + +struct Node { + next: Option>>, + value: T, +} + +fn clear(head: &mut Option>>) { + match head.take() { + Some(node) => *head = node.next, + None => (), + } +} + +fn main() {} diff --git a/src/test/run-pass/issue-36053.rs b/src/test/run-pass/issue-36053.rs new file mode 100644 index 0000000000000..2411996cf054b --- /dev/null +++ b/src/test/run-pass/issue-36053.rs @@ -0,0 +1,32 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Regression test for #36053. ICE was caused due to obligations being +// added to a special, dedicated fulfillment cx during a +// probe. Problem seems to be related to the particular definition of +// `FusedIterator` in std but I was not able to isolate that into an +// external crate. + +#![feature(fused)] +use std::iter::FusedIterator; + +struct Thing<'a>(&'a str); +impl<'a> Iterator for Thing<'a> { + type Item = &'a str; + fn next(&mut self) -> Option<&'a str> { + None + } +} + +impl<'a> FusedIterator for Thing<'a> {} + +fn main() { + Thing("test").fuse().filter(|_| true).count(); +} diff --git a/src/test/run-pass/typeid-intrinsic.rs b/src/test/run-pass/typeid-intrinsic.rs index e99a5f69af40f..36650368d57be 100644 --- a/src/test/run-pass/typeid-intrinsic.rs +++ b/src/test/run-pass/typeid-intrinsic.rs @@ -78,4 +78,13 @@ pub fn main() { b.hash(&mut s2); assert_eq!(s1.finish(), s2.finish()); + + // Check projections + + assert_eq!(TypeId::of::(), other1::id_i32_iterator()); + assert_eq!(TypeId::of::(), other1::id_u32_iterator()); + assert_eq!(other1::id_i32_iterator(), other2::id_i32_iterator()); + assert_eq!(other1::id_u32_iterator(), other2::id_u32_iterator()); + assert!(other1::id_i32_iterator() != other1::id_u32_iterator()); + assert!(TypeId::of::() != TypeId::of::()); } diff --git a/src/test/run-pass/union/union-c-interop.rs b/src/test/run-pass/union/union-c-interop.rs index a9f97620ebd46..bea4d5f923e21 100644 --- a/src/test/run-pass/union/union-c-interop.rs +++ b/src/test/run-pass/union/union-c-interop.rs @@ -10,14 +10,14 @@ #![feature(untagged_unions)] -#[derive(Copy)] +#[derive(Clone, Copy)] #[repr(C)] struct LARGE_INTEGER_U { LowPart: u32, HighPart: u32, } -#[derive(Copy)] +#[derive(Clone, Copy)] #[repr(C)] union LARGE_INTEGER { __unnamed__: LARGE_INTEGER_U, @@ -25,9 +25,6 @@ union LARGE_INTEGER { QuadPart: u64, } -impl Clone for LARGE_INTEGER_U { fn clone(&self) -> Self { *self } } -impl Clone for LARGE_INTEGER { fn clone(&self) -> Self { *self } } - #[link(name = "rust_test_helpers")] extern "C" { fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER; diff --git a/src/test/run-pass/union/union-derive.rs b/src/test/run-pass/union/union-derive.rs index b71c23990a474..8ff6f17394f9e 100644 --- a/src/test/run-pass/union/union-derive.rs +++ b/src/test/run-pass/union/union-derive.rs @@ -14,18 +14,34 @@ #[derive( Copy, + Clone, + Eq, )] union U { a: u8, b: u16, } -impl Clone for U { - fn clone(&self) -> Self { *self } +impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive( + Clone, + Copy, + Eq +)] +union W { + a: T, } +impl PartialEq for W { fn eq(&self, rhs: &Self) -> bool { true } } + fn main() { let u = U { b: 0 }; let u1 = u; let u2 = u.clone(); + assert!(u1 == u2); + + let w = W { a: 0 }; + let w1 = w.clone(); + assert!(w == w1); }