Auto merge of #36491 - Manishearth:rollup, r=Manishearth
Rollup of 9 pull requests - Successful merges: #36384, #36405, #36425, #36429, #36438, #36454, #36459, #36461, #36463 - Failed merges: #36444
This commit is contained in:
commit
dc75933aba
62 changed files with 1397 additions and 975 deletions
|
@ -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<T: Clone + ?Sized> { _field: ::marker::PhantomData<T> }
|
||||
#[doc(hidden)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
#[unstable(feature = "derive_clone_copy",
|
||||
reason = "deriving hack, should not be public",
|
||||
issue = "0")]
|
||||
pub struct AssertParamIsCopy<T: Copy + ?Sized> { _field: ::marker::PhantomData<T> }
|
||||
#[cfg(stage0)]
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
#[unstable(feature = "derive_clone_copy",
|
||||
|
|
|
@ -129,7 +129,7 @@ pub trait PartialEq<Rhs: ?Sized = Self> {
|
|||
/// 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<Self> {
|
|||
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<T: Eq + ?Sized> { _field: ::marker::PhantomData<T> }
|
||||
|
||||
/// An `Ordering` is the result of a comparison between two values.
|
||||
///
|
||||
/// # Examples
|
||||
|
|
|
@ -520,8 +520,8 @@ impl<T> ops::Index<usize> 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<T> ops::Index<usize> for [T] {
|
|||
impl<T> ops::IndexMut<usize> 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]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<F, R>(&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()");
|
||||
|
||||
|
|
|
@ -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<LoadedMacro>;
|
||||
}
|
||||
|
||||
/// Metadata encoding and decoding can make use of thread-local encoding and
|
||||
/// decoding contexts. These allow implementers of serialize::Encodable and
|
||||
|
|
|
@ -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<Option<ast::CrateNum>>,
|
||||
pub injected_panic_runtime: Cell<Option<ast::CrateNum>>,
|
||||
|
||||
/// Names of all bang-style macros and syntax extensions
|
||||
/// available in this crate
|
||||
pub available_macros: RefCell<HashSet<Name>>,
|
||||
|
||||
/// 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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -70,7 +70,11 @@ pub struct TraitDef<'tcx> {
|
|||
pub specialization_graph: RefCell<traits::specialization_graph::Graph>,
|
||||
|
||||
/// Various flags
|
||||
pub flags: Cell<TraitFlags>
|
||||
pub flags: Cell<TraitFlags>,
|
||||
|
||||
/// 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<Name>)
|
||||
associated_type_names: Vec<Name>,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<token::InternedString, Span>;
|
||||
|
||||
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<LoadedMacro> {
|
||||
|
|
|
@ -104,14 +104,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor<Vec<u8>>, 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);
|
||||
}
|
||||
|
|
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use 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<ast::NodeId, Vec<Mark>>,
|
||||
}
|
||||
|
||||
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<ast::Block>) -> P<ast::Block> {
|
||||
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<ast::Item>) -> SmallVector<P<ast::Item>> {
|
||||
match item.node {
|
||||
ast::ItemKind::Mac(..) => SmallVector::zero(),
|
||||
_ => fold::noop_fold_item(item, self),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Name>,
|
||||
|
||||
// Maps the `Mark` of an expansion to its containing module or block.
|
||||
expansion_data: Vec<macros::ExpansionData>,
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
217
src/librustc_resolve/macros.rs
Normal file
217
src/librustc_resolve/macros.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use 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<ModuleData>,
|
||||
}
|
||||
|
||||
// FIXME(jseyfried): merge with `::ModuleS`.
|
||||
#[derive(Default)]
|
||||
struct ModuleData {
|
||||
parent: Option<Rc<ModuleData>>,
|
||||
macros: RefCell<FnvHashMap<Name, Rc<SyntaxExtension>>>,
|
||||
macros_escape: bool,
|
||||
}
|
||||
|
||||
impl<'a> base::Resolver for Resolver<'a> {
|
||||
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro> {
|
||||
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<SyntaxExtension>) {
|
||||
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<Mark>) {
|
||||
self.macros_at_scope.insert(id, macros);
|
||||
}
|
||||
|
||||
fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
|
||||
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<Rc<SyntaxExtension>> {
|
||||
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<ModuleData>,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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<Duration> {
|
||||
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<Duration> {
|
||||
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<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;
|
||||
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<Duration> {
|
||||
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<u32> 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<u32> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
|
||||
fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(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)
|
||||
}
|
||||
pub trait Resolver {
|
||||
fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec<LoadedMacro>;
|
||||
fn next_node_id(&mut self) -> ast::NodeId;
|
||||
|
||||
let mut syntax_expanders = SyntaxEnv::new();
|
||||
syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
|
||||
fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion);
|
||||
fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc<SyntaxExtension>);
|
||||
fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>);
|
||||
|
||||
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<LoadedMacro>;
|
||||
fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>;
|
||||
fn resolve_invoc(&mut self, invoc: &Invocation) -> Option<Rc<SyntaxExtension>>;
|
||||
}
|
||||
|
||||
pub enum LoadedMacro {
|
||||
|
@ -556,11 +479,35 @@ pub enum LoadedMacro {
|
|||
CustomDerive(String, Box<MultiItemModifier>),
|
||||
}
|
||||
|
||||
pub struct DummyMacroLoader;
|
||||
impl MacroLoader for DummyMacroLoader {
|
||||
fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec<LoadedMacro> {
|
||||
pub struct DummyResolver;
|
||||
|
||||
impl Resolver for DummyResolver {
|
||||
fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec<LoadedMacro> {
|
||||
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<SyntaxExtension>) {}
|
||||
fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {}
|
||||
|
||||
fn find_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None }
|
||||
fn resolve_invoc(&mut self, _invoc: &Invocation) -> Option<Rc<SyntaxExtension>> { None }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModuleData {
|
||||
pub mod_path: Vec<ast::Ident>,
|
||||
pub directory: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExpansionData {
|
||||
pub mark: Mark,
|
||||
pub depth: usize,
|
||||
pub backtrace: ExpnId,
|
||||
pub module: Rc<ModuleData>,
|
||||
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<ast::MacroDef>,
|
||||
|
||||
pub syntax_env: SyntaxEnv,
|
||||
pub derive_modes: HashMap<InternedString, Box<MultiItemModifier>>,
|
||||
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<MultiItemModifier>,
|
||||
sp: Span) {
|
||||
pub fn insert_custom_derive(&mut self, name: &str, ext: Box<MultiItemModifier>, 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<NamedSyntaxExtension>, 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<ast::Expr>, err_msg: &str)
|
||||
-> Option<(InternedString, ast::StrStyle)> {
|
||||
pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str)
|
||||
-> Option<Spanned<(InternedString, ast::StrStyle)>> {
|
||||
// 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<ast::Expr>, 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<ast::Expr>, err_msg: &str)
|
|||
None
|
||||
}
|
||||
|
||||
pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, 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<ModuleData>,
|
||||
pub current_module: Module,
|
||||
|
||||
/// All bang-style macro/extension names
|
||||
/// encountered so far; to be used for diagnostics in resolve
|
||||
pub names: HashSet<Name>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Module(u32);
|
||||
|
||||
struct ModuleData {
|
||||
parent: Module,
|
||||
paths: Rc<ModulePaths>,
|
||||
macros: HashMap<Name, Rc<SyntaxExtension>>,
|
||||
macros_escape: bool,
|
||||
in_block: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModulePaths {
|
||||
pub mod_path: Vec<ast::Ident>,
|
||||
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<ModulePaths> {
|
||||
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<ModulePaths>)
|
||||
-> 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<Rc<SyntaxExtension>> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,6 +97,7 @@ pub trait AstBuilder {
|
|||
typ: P<ast::Ty>,
|
||||
ex: P<ast::Expr>)
|
||||
-> P<ast::Stmt>;
|
||||
fn stmt_let_type_only(&self, span: Span, ty: P<ast::Ty>) -> ast::Stmt;
|
||||
fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> 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::Ty>) -> 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::Item>) -> ast::Stmt {
|
||||
ast::Stmt {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
|
|
|
@ -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<P<ast::Expr>>), $( $kind($ty), )* }
|
||||
|
@ -77,6 +79,17 @@ macro_rules! expansions {
|
|||
}, )*)*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_with<V: Visitor>(&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<ast::Expr> [], "expression", .make_expr, .fold_expr;
|
||||
Pat: P<ast::Pat> [], "pattern", .make_pat, .fold_pat;
|
||||
Ty: P<ast::Ty> [], "type", .make_ty, .fold_ty;
|
||||
Expr: P<ast::Expr> [], "expression", .make_expr, .fold_expr, .visit_expr;
|
||||
Pat: P<ast::Pat> [], "pattern", .make_pat, .fold_pat, .visit_pat;
|
||||
Ty: P<ast::Ty> [], "type", .make_ty, .fold_ty, .visit_ty;
|
||||
Stmts: SmallVector<ast::Stmt> [SmallVector, ast::Stmt],
|
||||
"statement", .make_stmts, lift .fold_stmt;
|
||||
"statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
|
||||
Items: SmallVector<P<ast::Item>> [SmallVector, P<ast::Item>],
|
||||
"item", .make_items, lift .fold_item;
|
||||
"item", .make_items, lift .fold_item, lift .visit_item;
|
||||
TraitItems: SmallVector<ast::TraitItem> [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<ast::ImplItem> [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<ast::Attribute>,
|
||||
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<SyntaxExtension>) -> 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<SyntaxExtension>) -> 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<SyntaxExtension>) -> 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<Invocation>,
|
||||
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<T: HasAttrs>(&self, mut item: T) -> (T, Option<ast::Attribute>) {
|
||||
fn classify_item<T: HasAttrs>(&mut self, mut item: T) -> (T, Option<ast::Attribute>) {
|
||||
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<T: HasAttrs>(&mut self, node: T) -> Option<T> {
|
||||
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<Block>) -> P<Block> {
|
||||
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<NamedSyntaxExtension>,
|
||||
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<TokenTree> {
|
||||
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<ast::Path> ,
|
||||
}
|
||||
|
||||
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(
|
||||
"<test>".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(
|
||||
"<test>".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(
|
||||
"<test>".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.
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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<ast::NodeId, Expansion>,
|
||||
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<ast::Item>) -> SmallVector<P<ast::Item>> {
|
||||
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<ast::Block>) -> P<ast::Block> {
|
||||
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 {
|
||||
|
|
|
@ -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::MacResult+'static> {
|
||||
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::<Vec<String>>().join("::");
|
||||
let mod_path = &cx.current_expansion.module.mod_path;
|
||||
let string = mod_path.iter().map(|x| x.to_string()).collect::<Vec<String>>().join("::");
|
||||
|
||||
base::MacEager::expr(cx.expr_str(
|
||||
sp,
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
@ -1320,51 +1320,27 @@ pub fn noop_fold_exprs<T: Folder>(es: Vec<P<Expr>>, folder: &mut T) -> Vec<P<Exp
|
|||
es.move_flat_map(|e| folder.fold_opt_expr(e))
|
||||
}
|
||||
|
||||
pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T)
|
||||
-> SmallVector<Stmt> {
|
||||
pub fn noop_fold_stmt<T: Folder>(Stmt {node, span, id}: Stmt, folder: &mut T) -> SmallVector<Stmt> {
|
||||
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<T: Folder>(node: StmtKind, folder: &mut T) -> SmallVector<StmtKind> {
|
||||
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())
|
||||
}))),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ast::Ident>,
|
|||
}).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<InternedString>,
|
||||
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<ast::Item>, Option<P<ast::Item>>) {
|
|||
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 <ident> = __test::main`
|
||||
let reexport_ident = token::str_to_ident(&s);
|
||||
|
@ -529,14 +529,14 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P<ast::Item>, Option<P<ast::Item>>) {
|
|||
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));
|
||||
|
|
|
@ -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") => {
|
||||
|
||||
bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
|
||||
unify_fieldless_variants = true;
|
||||
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("Clone", c, s, sub, Mode::Shallow)
|
||||
cs_clone_shallow("Clone", c, s, sub, false)
|
||||
}));
|
||||
}
|
||||
ItemKind::Union(..) => {
|
||||
bounds = vec![Literal(path_std!(cx, core::marker::Copy))];
|
||||
is_shallow = true;
|
||||
substructure = combine_substructure(Box::new(|c, s, sub| {
|
||||
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<Expr> {
|
||||
fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>,
|
||||
ty: P<ast::Ty>, span: Span, helper_name: &str) {
|
||||
// Generate statement `let _: helper_name<ty>;`,
|
||||
// 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<ast::Stmt>, variant: &VariantData) {
|
||||
for field in variant.fields() {
|
||||
// let _: AssertParamIsClone<FieldTy>;
|
||||
assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
|
||||
}
|
||||
}
|
||||
|
||||
let mut stmts = Vec::new();
|
||||
if is_union {
|
||||
// let _: AssertParamIsCopy<Self>;
|
||||
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<Expr> {
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Expr> {
|
||||
cs_same_method(|cx, span, exprs| {
|
||||
// create `a.<method>(); b.<method>(); c.<method>(); ...`
|
||||
// (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<Expr> {
|
||||
fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec<ast::Stmt>,
|
||||
ty: P<ast::Ty>, span: Span, helper_name: &str) {
|
||||
// Generate statement `let _: helper_name<ty>;`,
|
||||
// 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<ast::Stmt>, variant: &ast::VariantData) {
|
||||
for field in variant.fields() {
|
||||
// let _: AssertParamIsEq<FieldTy>;
|
||||
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))
|
||||
}
|
||||
|
|
|
@ -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<ast::Item> {
|
||||
let field_tys: Vec<P<ast::Ty>> = 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<ast::Item> {
|
||||
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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
deriving::register_all(env);
|
||||
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)));
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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 {
|
||||
{
|
||||
|
|
|
@ -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 };
|
||||
|
|
21
src/test/compile-fail/issue-36053-2.rs
Normal file
21
src/test/compile-fail/issue-36053-2.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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
|
||||
}
|
46
src/test/compile-fail/macro-expansion-tests.rs
Normal file
46
src/test/compile-fail/macro-expansion-tests.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
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() {}
|
|
@ -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
|
||||
|
||||
|
|
41
src/test/compile-fail/union/union-derive-clone.rs
Normal file
41
src/test/compile-fail/union/union-derive-clone.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(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<T> {
|
||||
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<CloneNoCopy>`
|
||||
}
|
30
src/test/compile-fail/union/union-derive-eq.rs
Normal file
30
src/test/compile-fail/union/union-derive-eq.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(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() {}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -22,6 +22,8 @@ pub type F = Option<isize>;
|
|||
pub type G = usize;
|
||||
pub type H = &'static str;
|
||||
pub type I = Box<Fn()>;
|
||||
pub type I32Iterator = Iterator<Item=i32>;
|
||||
pub type U32Iterator = Iterator<Item=u32>;
|
||||
|
||||
pub fn id_A() -> TypeId { TypeId::of::<A>() }
|
||||
pub fn id_B() -> TypeId { TypeId::of::<B>() }
|
||||
|
@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::<H>() }
|
|||
pub fn id_I() -> TypeId { TypeId::of::<I>() }
|
||||
|
||||
pub fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
|
||||
|
||||
pub fn id_i32_iterator() -> TypeId { TypeId::of::<I32Iterator>() }
|
||||
pub fn id_u32_iterator() -> TypeId { TypeId::of::<U32Iterator>() }
|
||||
|
|
|
@ -22,6 +22,8 @@ pub type F = Option<isize>;
|
|||
pub type G = usize;
|
||||
pub type H = &'static str;
|
||||
pub type I = Box<Fn()>;
|
||||
pub type I32Iterator = Iterator<Item=i32>;
|
||||
pub type U32Iterator = Iterator<Item=u32>;
|
||||
|
||||
pub fn id_A() -> TypeId { TypeId::of::<A>() }
|
||||
pub fn id_B() -> TypeId { TypeId::of::<B>() }
|
||||
|
@ -34,3 +36,6 @@ pub fn id_H() -> TypeId { TypeId::of::<H>() }
|
|||
pub fn id_I() -> TypeId { TypeId::of::<I>() }
|
||||
|
||||
pub fn foo<T: Any>() -> TypeId { TypeId::of::<T>() }
|
||||
|
||||
pub fn id_i32_iterator() -> TypeId { TypeId::of::<I32Iterator>() }
|
||||
pub fn id_u32_iterator() -> TypeId { TypeId::of::<U32Iterator>() }
|
||||
|
|
28
src/test/run-pass/issue-35546.rs
Normal file
28
src/test/run-pass/issue-35546.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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<Send>`).
|
||||
|
||||
struct Node<T: ?Sized + Send> {
|
||||
next: Option<Box<Node<Send>>>,
|
||||
value: T,
|
||||
}
|
||||
|
||||
fn clear(head: &mut Option<Box<Node<Send>>>) {
|
||||
match head.take() {
|
||||
Some(node) => *head = node.next,
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
32
src/test/run-pass/issue-36053.rs
Normal file
32
src/test/run-pass/issue-36053.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// 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();
|
||||
}
|
|
@ -78,4 +78,13 @@ pub fn main() {
|
|||
b.hash(&mut s2);
|
||||
|
||||
assert_eq!(s1.finish(), s2.finish());
|
||||
|
||||
// Check projections
|
||||
|
||||
assert_eq!(TypeId::of::<other1::I32Iterator>(), other1::id_i32_iterator());
|
||||
assert_eq!(TypeId::of::<other1::U32Iterator>(), 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::<other1::I32Iterator>() != TypeId::of::<other1::U32Iterator>());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<T> {
|
||||
a: T,
|
||||
}
|
||||
|
||||
impl<T> PartialEq for W<T> { 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);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue