From 328b47d83720f011cb1c95685c3e7df8195efbd3 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 25 Dec 2013 11:10:33 -0700 Subject: [PATCH] Load macros from external modules --- src/compiletest/header.rs | 4 + src/librustc/driver/driver.rs | 26 +++- src/librustc/driver/session.rs | 1 + src/librustc/front/feature_gate.rs | 20 ++- src/librustc/front/test.rs | 14 +- src/librustc/metadata/common.rs | 4 + src/librustc/metadata/creader.rs | 135 ++++++++++++++---- src/librustc/metadata/csearch.rs | 13 ++ src/librustc/metadata/cstore.rs | 17 ++- src/librustc/metadata/decoder.rs | 17 +++ src/librustc/metadata/encoder.rs | 86 +++++++++-- src/librustc/middle/astencode.rs | 14 ++ src/librustc/middle/lint.rs | 7 +- src/librustc/middle/resolve.rs | 11 +- src/librustc/middle/typeck/collect.rs | 5 +- src/librustdoc/core.rs | 4 +- src/librustdoc/test.rs | 4 +- src/librustpkg/lib.rs | 7 +- src/librustpkg/util.rs | 118 +++++++++------ src/libsyntax/ext/base.rs | 111 +++++++------- src/libsyntax/ext/build.rs | 4 +- src/libsyntax/ext/deriving/generic.rs | 2 +- src/libsyntax/ext/expand.rs | 110 ++++++++++++-- src/libsyntax/ext/format.rs | 2 +- src/libsyntax/ext/quote.rs | 2 +- src/libsyntax/ext/registrar.rs | 60 ++++++++ src/libsyntax/fold.rs | 37 +++-- src/libsyntax/lib.rs | 1 + src/test/auxiliary/macro_crate_def_only.rs | 16 +++ src/test/auxiliary/macro_crate_test.rs | 42 ++++++ .../compile-fail/gated-macro_registrar.rs | 15 ++ src/test/compile-fail/gated-phase.rs | 17 +++ .../macro-crate-unexported-macro.rs | 21 +++ .../compile-fail/macro-crate-unknown-crate.rs | 16 +++ .../compile-fail/multiple-macro-registrars.rs | 22 +++ .../phase-syntax-doesnt-resolve.rs | 24 ++++ src/test/run-pass/macro-crate-def-only.rs | 21 +++ src/test/run-pass/macro-crate.rs | 23 +++ .../phase-syntax-link-does-resolve.rs | 23 +++ 39 files changed, 872 insertions(+), 204 deletions(-) create mode 100644 src/libsyntax/ext/registrar.rs create mode 100644 src/test/auxiliary/macro_crate_def_only.rs create mode 100644 src/test/auxiliary/macro_crate_test.rs create mode 100644 src/test/compile-fail/gated-macro_registrar.rs create mode 100644 src/test/compile-fail/gated-phase.rs create mode 100644 src/test/compile-fail/macro-crate-unexported-macro.rs create mode 100644 src/test/compile-fail/macro-crate-unknown-crate.rs create mode 100644 src/test/compile-fail/multiple-macro-registrars.rs create mode 100644 src/test/compile-fail/phase-syntax-doesnt-resolve.rs create mode 100644 src/test/run-pass/macro-crate-def-only.rs create mode 100644 src/test/run-pass/macro-crate.rs create mode 100644 src/test/run-pass/phase-syntax-link-does-resolve.rs diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index d4a4f38cc63..fa99b89c252 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -90,10 +90,14 @@ pub fn is_test_ignored(config: &config, testfile: &Path) -> bool { fn xfail_target(config: &config) -> ~str { ~"xfail-" + util::get_os(config.target) } + fn xfail_stage(config: &config) -> ~str { + ~"xfail-" + config.stage_id.split('-').next().unwrap() + } let val = iter_header(testfile, |ln| { if parse_name_directive(ln, "xfail-test") { false } else if parse_name_directive(ln, xfail_target(config)) { false } + else if parse_name_directive(ln, xfail_stage(config)) { false } else if config.mode == common::mode_pretty && parse_name_directive(ln, "xfail-pretty") { false } else { true } diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index aa3ab80b487..7e13bd2fcf1 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -20,6 +20,7 @@ use lib::llvm::{ContextRef, ModuleRef}; use metadata::common::LinkMeta; use metadata::{creader, filesearch}; use metadata::cstore::CStore; +use metadata::creader::Loader; use metadata; use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable}; use middle; @@ -41,6 +42,7 @@ use syntax::attr; use syntax::attr::{AttrMetaMethods}; use syntax::codemap; use syntax::diagnostic; +use syntax::ext::base::CrateLoader; use syntax::parse; use syntax::parse::token; use syntax::print::{pp, pprust}; @@ -163,6 +165,7 @@ pub fn phase_1_parse_input(sess: Session, cfg: ast::CrateConfig, input: &input) /// standard library and prelude. pub fn phase_2_configure_and_expand(sess: Session, cfg: ast::CrateConfig, + loader: &mut CrateLoader, mut crate: ast::Crate) -> (ast::Crate, syntax::ast_map::Map) { let time_passes = sess.time_passes(); @@ -188,9 +191,14 @@ pub fn phase_2_configure_and_expand(sess: Session, crate = time(time_passes, "configuration 1", crate, |crate| front::config::strip_unconfigured_items(crate)); - crate = time(time_passes, "expansion", crate, |crate| - syntax::ext::expand::expand_crate(sess.parse_sess, cfg.clone(), - crate)); + crate = time(time_passes, "expansion", crate, |crate| { + syntax::ext::expand::expand_crate(sess.parse_sess, + loader, + cfg.clone(), + crate) + }); + // dump the syntax-time crates + sess.cstore.reset(); // strip again, in case expansion added anything with a #[cfg]. crate = time(time_passes, "configuration 2", crate, |crate| @@ -248,6 +256,11 @@ pub fn phase_3_run_analysis_passes(sess: Session, time(time_passes, "looking for entry point", (), |_| middle::entry::find_entry_point(sess, crate, ast_map)); + sess.macro_registrar_fn.with_mut(|r| *r = + time(time_passes, "looking for macro registrar", (), |_| + syntax::ext::registrar::find_macro_registrar( + sess.span_diagnostic, crate))); + let freevars = time(time_passes, "freevar finding", (), |_| freevars::annotate_freevars(def_map, crate)); @@ -491,7 +504,8 @@ pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &input, let (expanded_crate, ast_map) = { let crate = phase_1_parse_input(sess, cfg.clone(), input); if stop_after_phase_1(sess) { return; } - phase_2_configure_and_expand(sess, cfg, crate) + let loader = &mut Loader::new(sess); + phase_2_configure_and_expand(sess, cfg, loader, crate) }; let outputs = build_output_filenames(input, outdir, output, expanded_crate.attrs, sess); @@ -579,7 +593,8 @@ pub fn pretty_print_input(sess: Session, let (crate, ast_map, is_expanded) = match ppm { PpmExpanded | PpmExpandedIdentified | PpmTyped => { - let (crate, ast_map) = phase_2_configure_and_expand(sess, cfg, crate); + let loader = &mut Loader::new(sess); + let (crate, ast_map) = phase_2_configure_and_expand(sess, cfg, loader, crate); (crate, Some(ast_map), true) } _ => (crate, None, false) @@ -912,6 +927,7 @@ pub fn build_session_(sopts: @session::options, // For a library crate, this is always none entry_fn: RefCell::new(None), entry_type: Cell::new(None), + macro_registrar_fn: RefCell::new(None), span_diagnostic: span_diagnostic_handler, filesearch: filesearch, building_library: Cell::new(false), diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index d701fed1a52..abebfc614a8 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -210,6 +210,7 @@ pub struct Session_ { entry_fn: RefCell>, entry_type: Cell>, span_diagnostic: @diagnostic::SpanHandler, + macro_registrar_fn: RefCell>, filesearch: @filesearch::FileSearch, building_library: Cell, working_dir: Path, diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index c0732e93bee..d51f3e3f037 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -43,6 +43,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("non_ascii_idents", Active), ("thread_local", Active), ("link_args", Active), + ("phase", Active), + ("macro_registrar", Active), // These are used to test this portion of the compiler, they don't actually // mean anything @@ -114,7 +116,15 @@ impl Visitor<()> for Context { } } } - _ => {} + ast::ViewItemExternMod(..) => { + for attr in i.attrs.iter() { + if "phase" == attr.name() { + self.gate_feature("phase", attr.span, + "compile time crate loading is \ + experimental and possibly buggy"); + } + } + } } visit::walk_view_item(self, i, ()) } @@ -151,6 +161,14 @@ impl Visitor<()> for Context { } } + ast::ItemFn(..) => { + if attr::contains_name(i.attrs, "macro_registrar") { + self.gate_feature("macro_registrar", i.span, + "cross-crate macro exports are \ + experimental and possibly buggy"); + } + } + _ => {} } diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index b9a98e7b15a..95bf171562d 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -14,6 +14,7 @@ use driver::session; use front::config; use front::std_inject::with_version; +use metadata::creader::Loader; use std::cell::RefCell; use std::vec; @@ -38,10 +39,10 @@ struct Test { should_fail: bool } -struct TestCtxt { +struct TestCtxt<'a> { sess: session::Session, path: RefCell<~[ast::Ident]>, - ext_cx: ExtCtxt, + ext_cx: ExtCtxt<'a>, testfns: RefCell<~[Test]>, is_extra: bool, config: ast::CrateConfig, @@ -63,11 +64,11 @@ pub fn modify_for_testing(sess: session::Session, } } -struct TestHarnessGenerator { - cx: TestCtxt, +struct TestHarnessGenerator<'a> { + cx: TestCtxt<'a>, } -impl fold::Folder for TestHarnessGenerator { +impl<'a> fold::Folder for TestHarnessGenerator<'a> { fn fold_crate(&mut self, c: ast::Crate) -> ast::Crate { let folded = fold::noop_fold_crate(c, self); @@ -155,9 +156,10 @@ impl fold::Folder for TestHarnessGenerator { fn generate_test_harness(sess: session::Session, crate: ast::Crate) -> ast::Crate { + let loader = &mut Loader::new(sess); let mut cx: TestCtxt = TestCtxt { sess: sess, - ext_cx: ExtCtxt::new(sess.parse_sess, sess.opts.cfg.clone()), + ext_cx: ExtCtxt::new(sess.parse_sess, sess.opts.cfg.clone(), loader), path: RefCell::new(~[]), testfns: RefCell::new(~[]), is_extra: is_extra(&crate), diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 2d86a36dd60..09531e80ae5 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -204,6 +204,10 @@ pub static tag_native_libraries_lib: uint = 0x104; pub static tag_native_libraries_name: uint = 0x105; pub static tag_native_libraries_kind: uint = 0x106; +pub static tag_macro_registrar_fn: uint = 0x110; +pub static tag_exported_macros: uint = 0x111; +pub static tag_macro_def: uint = 0x112; + #[deriving(Clone)] pub struct LinkMeta { crateid: CrateId, diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 6508da88f3d..1d992833112 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -10,10 +10,13 @@ //! Validates all used crates and extern libraries and loads their metadata +use driver::{driver, session}; use driver::session::Session; +use metadata::csearch; use metadata::cstore; use metadata::decoder; use metadata::loader; +use metadata::loader::Os; use std::cell::RefCell; use std::hashmap::HashMap; @@ -23,6 +26,7 @@ use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::{Span, DUMMY_SP}; use syntax::diagnostic::SpanHandler; +use syntax::ext::base::{CrateLoader, MacroCrate}; use syntax::parse::token; use syntax::parse::token::IdentInterner; use syntax::crateid::CrateId; @@ -131,37 +135,65 @@ fn visit_crate(e: &Env, c: &ast::Crate) { } fn visit_view_item(e: &mut Env, i: &ast::ViewItem) { + let should_load = i.attrs.iter().all(|attr| { + "phase" != attr.name() || + attr.meta_item_list().map_or(false, |phases| { + attr::contains_name(phases, "link") + }) + }); + + if !should_load { + return; + } + + match extract_crate_info(i) { + Some(info) => { + let cnum = resolve_crate(e, info.ident, info.name, info.version, + @"", i.span); + e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum); + } + None => () + } +} + +struct CrateInfo { + ident: @str, + name: @str, + version: @str, + id: ast::NodeId, +} + +fn extract_crate_info(i: &ast::ViewItem) -> Option { match i.node { - ast::ViewItemExternMod(ident, path_opt, id) => { - let ident = token::ident_to_str(&ident); - debug!("resolving extern mod stmt. ident: {:?} path_opt: {:?}", - ident, path_opt); - let (name, version) = match path_opt { - Some((path_str, _)) => { - let crateid: Option = from_str(path_str); - match crateid { - None => (@"", @""), - Some(crateid) => { - let version = match crateid.version { - None => @"", - Some(ref ver) => ver.to_managed(), - }; - (crateid.name.to_managed(), version) - } - } - } - None => (ident, @""), - }; - let cnum = resolve_crate(e, - ident, - name, - version, - @"", - i.span); - e.sess.cstore.add_extern_mod_stmt_cnum(id, cnum); - } - _ => () - } + ast::ViewItemExternMod(ident, path_opt, id) => { + let ident = token::ident_to_str(&ident); + debug!("resolving extern mod stmt. ident: {:?} path_opt: {:?}", + ident, path_opt); + let (name, version) = match path_opt { + Some((path_str, _)) => { + let crateid: Option = from_str(path_str); + match crateid { + None => (@"", @""), + Some(crateid) => { + let version = match crateid.version { + None => @"", + Some(ref ver) => ver.to_managed(), + }; + (crateid.name.to_managed(), version) + } + } + } + None => (ident, @""), + }; + Some(CrateInfo { + ident: ident, + name: name, + version: version, + id: id, + }) + } + _ => None + } } fn visit_item(e: &Env, i: &ast::Item) { @@ -355,3 +387,46 @@ fn resolve_crate_deps(e: &mut Env, cdata: &[u8]) -> cstore::cnum_map { } return @RefCell::new(cnum_map); } + +pub struct Loader { + priv env: Env, +} + +impl Loader { + pub fn new(sess: Session) -> Loader { + let os = driver::get_os(driver::host_triple()).unwrap(); + let os = session::sess_os_to_meta_os(os); + Loader { + env: Env { + sess: sess, + os: os, + crate_cache: @RefCell::new(~[]), + next_crate_num: 1, + intr: token::get_ident_interner(), + } + } + } +} + +impl CrateLoader for Loader { + fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate { + let info = extract_crate_info(crate).unwrap(); + let cnum = resolve_crate(&mut self.env, info.ident, info.name, + info.version, @"", crate.span); + let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap(); + MacroCrate { + lib: library.dylib, + cnum: cnum + } + } + + fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> ~[@ast::Item] { + csearch::get_exported_macros(self.env.sess.cstore, cnum) + } + + fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> { + let cstore = self.env.sess.cstore; + csearch::get_macro_registrar_fn(cstore, cnum) + .map(|did| csearch::get_symbol(cstore, did)) + } +} diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 6dad364e661..9955186da46 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -301,3 +301,16 @@ pub fn get_trait_of_method(cstore: @cstore::CStore, decoder::get_trait_of_method(cdata, def_id.node, tcx) } +pub fn get_macro_registrar_fn(cstore: @cstore::CStore, + crate_num: ast::CrateNum) + -> Option { + let cdata = cstore.get_crate_data(crate_num); + decoder::get_macro_registrar_fn(cdata) +} + +pub fn get_exported_macros(cstore: @cstore::CStore, + crate_num: ast::CrateNum) + -> ~[@ast::Item] { + let cdata = cstore.get_crate_data(crate_num); + decoder::get_exported_macros(cdata) +} diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index 5be69e9452c..024e214a2fe 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -53,7 +53,7 @@ pub enum NativeLibaryKind { // Where a crate came from on the local filesystem. One of these two options // must be non-None. -#[deriving(Eq)] +#[deriving(Eq, Clone)] pub struct CrateSource { dylib: Option, rlib: Option, @@ -123,6 +123,21 @@ impl CStore { } } + pub fn get_used_crate_source(&self, cnum: ast::CrateNum) + -> Option { + let mut used_crate_sources = self.used_crate_sources.borrow_mut(); + used_crate_sources.get().iter().find(|source| source.cnum == cnum) + .map(|source| source.clone()) + } + + pub fn reset(&self) { + self.metas.with_mut(|s| s.clear()); + self.extern_mod_crate_map.with_mut(|s| s.clear()); + self.used_crate_sources.with_mut(|s| s.clear()); + self.used_libraries.with_mut(|s| s.clear()); + self.used_link_args.with_mut(|s| s.clear()); + } + pub fn get_used_crates(&self, prefer: LinkagePreference) -> ~[(ast::CrateNum, Option)] { let used_crate_sources = self.used_crate_sources.borrow(); diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 77c8ddae3de..abf5b051c7d 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -23,6 +23,7 @@ use metadata::tydecode::{parse_ty_data, parse_def_id, use middle::ty::{ImplContainer, TraitContainer}; use middle::ty; use middle::typeck; +use middle::astencode; use middle::astencode::vtable_decoder_helpers; use std::at_vec; @@ -1275,3 +1276,19 @@ pub fn get_native_libraries(cdata: Cmd) -> ~[(cstore::NativeLibaryKind, ~str)] { }); return result; } + +pub fn get_macro_registrar_fn(cdata: Cmd) -> Option { + reader::maybe_get_doc(reader::Doc(cdata.data()), tag_macro_registrar_fn) + .map(|doc| item_def_id(doc, cdata)) +} + +pub fn get_exported_macros(cdata: Cmd) -> ~[@ast::Item] { + let macros = reader::get_doc(reader::Doc(cdata.data()), + tag_exported_macros); + let mut result = ~[]; + reader::tagged_docs(macros, tag_macro_def, |macro_doc| { + result.push(astencode::decode_exported_macro(macro_doc)); + true + }); + result +} diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 465a52221d9..c55318f9e5a 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -79,6 +79,8 @@ struct Stats { dep_bytes: Cell, lang_item_bytes: Cell, native_lib_bytes: Cell, + macro_registrar_fn_bytes: Cell, + macro_defs_bytes: Cell, impl_bytes: Cell, misc_bytes: Cell, item_bytes: Cell, @@ -1287,7 +1289,9 @@ fn encode_info_for_item(ecx: &EncodeContext, // Encode inherent implementations for this trait. encode_inherent_implementations(ecx, ebml_w, def_id); } - ItemMac(..) => fail!("item macros unimplemented") + ItemMac(..) => { + // macros are encoded separately + } } } @@ -1691,6 +1695,50 @@ fn encode_native_libraries(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) { ebml_w.end_tag(); } +fn encode_macro_registrar_fn(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) { + let ptr = ecx.tcx.sess.macro_registrar_fn.borrow(); + match *ptr.get() { + Some(did) => { + ebml_w.start_tag(tag_macro_registrar_fn); + encode_def_id(ebml_w, did); + ebml_w.end_tag(); + } + None => {} + } +} + +struct MacroDefVisitor<'a, 'b> { + ecx: &'a EncodeContext<'a>, + ebml_w: &'a mut writer::Encoder<'b> +} + +impl<'a, 'b> Visitor<()> for MacroDefVisitor<'a, 'b> { + fn visit_item(&mut self, item: &Item, _: ()) { + match item.node { + ItemMac(..) => { + self.ebml_w.start_tag(tag_macro_def); + astencode::encode_exported_macro(self.ebml_w, item); + self.ebml_w.end_tag(); + } + _ => {} + } + } +} + +fn encode_macro_defs(ecx: &EncodeContext, + crate: &Crate, + ebml_w: &mut writer::Encoder) { + ebml_w.start_tag(tag_exported_macros); + { + let mut visitor = MacroDefVisitor { + ecx: ecx, + ebml_w: ebml_w, + }; + visit::walk_crate(&mut visitor, crate, ()); + } + ebml_w.end_tag(); +} + struct ImplVisitor<'a,'b> { ecx: &'a EncodeContext<'a>, ebml_w: &'a mut writer::Encoder<'b>, @@ -1815,6 +1863,8 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, crate: &Crate) dep_bytes: Cell::new(0), lang_item_bytes: Cell::new(0), native_lib_bytes: Cell::new(0), + macro_registrar_fn_bytes: Cell::new(0), + macro_defs_bytes: Cell::new(0), impl_bytes: Cell::new(0), misc_bytes: Cell::new(0), item_bytes: Cell::new(0), @@ -1873,6 +1923,16 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, crate: &Crate) encode_native_libraries(&ecx, &mut ebml_w); ecx.stats.native_lib_bytes.set(ebml_w.writer.tell() - i); + // Encode the macro registrar function + i = ebml_w.writer.tell(); + encode_macro_registrar_fn(&ecx, &mut ebml_w); + ecx.stats.macro_registrar_fn_bytes.set(ebml_w.writer.tell() - i); + + // Encode macro definitions + i = ebml_w.writer.tell(); + encode_macro_defs(&ecx, crate, &mut ebml_w); + ecx.stats.macro_defs_bytes.set(ebml_w.writer.tell() - i); + // Encode the def IDs of impls, for coherence checking. i = ebml_w.writer.tell(); encode_impls(&ecx, crate, &mut ebml_w); @@ -1905,17 +1965,19 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, crate: &Crate) } println!("metadata stats:"); - println!(" inline bytes: {}", ecx.stats.inline_bytes.get()); - println!(" attribute bytes: {}", ecx.stats.attr_bytes.get()); - println!(" dep bytes: {}", ecx.stats.dep_bytes.get()); - println!(" lang item bytes: {}", ecx.stats.lang_item_bytes.get()); - println!(" native bytes: {}", ecx.stats.native_lib_bytes.get()); - println!(" impl bytes: {}", ecx.stats.impl_bytes.get()); - println!(" misc bytes: {}", ecx.stats.misc_bytes.get()); - println!(" item bytes: {}", ecx.stats.item_bytes.get()); - println!(" index bytes: {}", ecx.stats.index_bytes.get()); - println!(" zero bytes: {}", ecx.stats.zero_bytes.get()); - println!(" total bytes: {}", ecx.stats.total_bytes.get()); + println!(" inline bytes: {}", ecx.stats.inline_bytes.get()); + println!(" attribute bytes: {}", ecx.stats.attr_bytes.get()); + println!(" dep bytes: {}", ecx.stats.dep_bytes.get()); + println!(" lang item bytes: {}", ecx.stats.lang_item_bytes.get()); + println!(" native bytes: {}", ecx.stats.native_lib_bytes.get()); + println!("macro registrar bytes: {}", ecx.stats.macro_registrar_fn_bytes.get()); + println!(" macro def bytes: {}", ecx.stats.macro_defs_bytes.get()); + println!(" impl bytes: {}", ecx.stats.impl_bytes.get()); + println!(" misc bytes: {}", ecx.stats.misc_bytes.get()); + println!(" item bytes: {}", ecx.stats.item_bytes.get()); + println!(" index bytes: {}", ecx.stats.index_bytes.get()); + println!(" zero bytes: {}", ecx.stats.zero_bytes.get()); + println!(" total bytes: {}", ecx.stats.total_bytes.get()); } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 6a37324e05a..3e0436a8ec2 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -107,6 +107,13 @@ pub fn encode_inlined_item(ecx: &e::EncodeContext, ebml_w.writer.tell()); } +pub fn encode_exported_macro(ebml_w: &mut writer::Encoder, i: &ast::Item) { + match i.node { + ast::ItemMac(..) => encode_ast(ebml_w, ast::IIItem(@i.clone())), + _ => fail!("expected a macro") + } +} + pub fn decode_inlined_item(cdata: @cstore::crate_metadata, tcx: ty::ctxt, maps: Maps, @@ -159,6 +166,13 @@ pub fn decode_inlined_item(cdata: @cstore::crate_metadata, } } +pub fn decode_exported_macro(par_doc: ebml::Doc) -> @ast::Item { + match decode_ast(par_doc) { + ast::IIItem(item) => item, + _ => fail!("expected item") + } +} + // ______________________________________________________________________ // Enumerating the IDs which appear in an AST diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index 6da58c03b72..098e4154e17 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -924,15 +924,16 @@ static other_attrs: &'static [&'static str] = &[ "allow", "deny", "forbid", "warn", // lint options "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability "crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze", - "no_mangle", "no_send", "static_assert", "unsafe_no_drop_flag", - "packed", "simd", "repr", "deriving", "unsafe_destructor", "link", + "no_mangle", "no_send", "static_assert", "unsafe_no_drop_flag", "packed", + "simd", "repr", "deriving", "unsafe_destructor", "link", "phase", + "macro_export", //mod-level "path", "link_name", "link_args", "nolink", "macro_escape", "no_implicit_prelude", // fn-level "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start", - "no_split_stack", "cold", + "no_split_stack", "cold", "macro_registrar", // internal attribute: bypass privacy inside items "!resolve_unexported", diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 8731bf07ec3..2d1c5b1b4e7 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -1400,10 +1400,7 @@ impl Resolver { name_bindings.define_type(DefTrait(def_id), sp, is_public); new_parent } - - ItemMac(..) => { - fail!("item macros unimplemented") - } + ItemMac(..) => parent } } @@ -3804,9 +3801,9 @@ impl Resolver { }); } - ItemMac(..) => { - fail!("item macros unimplemented") - } + ItemMac(..) => { + // do nothing, these are just around to be encoded + } } } diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index eaf6cea244b..f9193c4f73c 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -560,7 +560,7 @@ pub fn convert(ccx: &CrateCtxt, it: &ast::Item) { debug!("convert: item {} with id {}", tcx.sess.str_of(it.ident), it.id); match it.node { // These don't define types. - ast::ItemForeignMod(_) | ast::ItemMod(_) => {} + ast::ItemForeignMod(_) | ast::ItemMod(_) | ast::ItemMac(_) => {} ast::ItemEnum(ref enum_definition, ref generics) => { ensure_no_ty_param_bounds(ccx, it.span, generics, "enumeration"); let tpt = ty_of_item(ccx, it); @@ -913,8 +913,7 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::Item) return tpt; } ast::ItemImpl(..) | ast::ItemMod(_) | - ast::ItemForeignMod(_) => fail!(), - ast::ItemMac(..) => fail!("item macros unimplemented") + ast::ItemForeignMod(_) | ast::ItemMac(_) => fail!(), } } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 947038bc8d8..e2205e12986 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -10,6 +10,7 @@ use rustc; use rustc::{driver, middle}; +use rustc::metadata::creader::Loader; use rustc::middle::privacy; use syntax::ast; @@ -74,7 +75,8 @@ fn get_ast_and_resolve(cpath: &Path, } let crate = phase_1_parse_input(sess, cfg.clone(), &input); - let (crate, ast_map) = phase_2_configure_and_expand(sess, cfg, crate); + let loader = &mut Loader::new(sess); + let (crate, ast_map) = phase_2_configure_and_expand(sess, cfg, loader, crate); let driver::driver::CrateAnalysis { exported_items, public_items, ty_cx, .. } = phase_3_run_analysis_passes(sess, &crate, ast_map); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 31a500718ee..ffc8388ee90 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -20,6 +20,7 @@ use extra::getopts; use extra::test; use rustc::driver::driver; use rustc::driver::session; +use rustc::metadata::creader::Loader; use syntax::diagnostic; use syntax::parse; @@ -58,7 +59,8 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int { let cfg = driver::build_configuration(sess); let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input); - let (crate, _) = driver::phase_2_configure_and_expand(sess, cfg, crate); + let loader = &mut Loader::new(sess); + let (crate, _) = driver::phase_2_configure_and_expand(sess, cfg, loader, crate); let ctx = @core::DocContext { crate: crate, diff --git a/src/librustpkg/lib.rs b/src/librustpkg/lib.rs index 5f86fa0e213..ab6f87c5a9d 100644 --- a/src/librustpkg/lib.rs +++ b/src/librustpkg/lib.rs @@ -28,6 +28,7 @@ pub use std::path::Path; use extra::workcache; use rustc::driver::{driver, session}; +use rustc::metadata::creader::Loader; use rustc::metadata::filesearch; use rustc::metadata::filesearch::rust_path; use rustc::util::sha2; @@ -118,7 +119,11 @@ impl<'a> PkgScript<'a> { @diagnostic::Emitter); let cfg = driver::build_configuration(sess); let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input); - let crate_and_map = driver::phase_2_configure_and_expand(sess, cfg.clone(), crate); + let loader = &mut Loader::new(sess); + let crate_and_map = driver::phase_2_configure_and_expand(sess, + cfg.clone(), + loader, + crate); let work_dir = build_pkg_id_in_workspace(id, workspace); debug!("Returning package script with id {}", id.to_str()); diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 99f1d9d67bc..e5308537250 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -16,11 +16,13 @@ use std::os; use std::io; use std::io::fs; use extra::workcache; +use rustc::metadata::creader::Loader; use rustc::driver::{driver, session}; use extra::getopts::groups::getopts; use syntax; use syntax::codemap::{DUMMY_SP, Spanned}; -use syntax::ext::base::ExtCtxt; +use syntax::ext::base; +use syntax::ext::base::{ExtCtxt, MacroCrate}; use syntax::{ast, attr, codemap, diagnostic, fold, visit}; use syntax::attr::AttrMetaMethods; use syntax::fold::Folder; @@ -63,9 +65,9 @@ struct ListenerFn { path: ~[ast::Ident] } -struct ReadyCtx { +struct ReadyCtx<'a> { sess: session::Session, - ext_cx: ExtCtxt, + ext_cx: ExtCtxt<'a>, path: ~[ast::Ident], fns: ~[ListenerFn] } @@ -130,7 +132,7 @@ fn fold_item(item: @ast::Item, fold: &mut CrateSetup) } struct CrateSetup<'a> { - ctx: &'a mut ReadyCtx, + ctx: &'a mut ReadyCtx<'a>, } impl<'a> fold::Folder for CrateSetup<'a> { @@ -145,9 +147,10 @@ impl<'a> fold::Folder for CrateSetup<'a> { /// Generate/filter main function, add the list of commands, etc. pub fn ready_crate(sess: session::Session, crate: ast::Crate) -> ast::Crate { + let loader = &mut Loader::new(sess); let mut ctx = ReadyCtx { sess: sess, - ext_cx: ExtCtxt::new(sess.parse_sess, sess.opts.cfg.clone()), + ext_cx: ExtCtxt::new(sess.parse_sess, sess.opts.cfg.clone(), loader), path: ~[], fns: ~[] }; @@ -269,23 +272,41 @@ pub fn compile_input(context: &BuildContext, // `extern mod` directives. let cfg = driver::build_configuration(sess); let crate = driver::phase_1_parse_input(sess, cfg.clone(), &input); - let (mut crate, ast_map) = driver::phase_2_configure_and_expand(sess, cfg.clone(), crate); - debug!("About to call find_and_install_dependencies..."); - - find_and_install_dependencies(context, pkg_id, in_file, sess, exec, &crate, deps, - |p| { - debug!("a dependency: {}", p.display()); - let mut addl_lib_search_paths = - addl_lib_search_paths.borrow_mut(); - let addl_lib_search_paths = - addl_lib_search_paths.get(); - let mut addl_lib_search_paths = - addl_lib_search_paths.borrow_mut(); - // Pass the directory containing a dependency - // as an additional lib search path - addl_lib_search_paths.get().insert(p); - }); + let (mut crate, ast_map) = { + let installer = CrateInstaller { + context: context, + parent: pkg_id, + parent_crate: in_file, + sess: sess, + exec: exec, + deps: deps, + save: |p| { + debug!("a dependency: {}", p.display()); + let mut addl_lib_search_paths = + addl_lib_search_paths.borrow_mut(); + let addl_lib_search_paths = + addl_lib_search_paths.get(); + let mut addl_lib_search_paths = + addl_lib_search_paths.borrow_mut(); + // Pass the directory containing a dependency + // as an additional lib search path + addl_lib_search_paths.get().insert(p); + }, + }; + let mut loader = CrateLoader { + installer: installer, + loader: Loader::new(sess), + }; + let (crate, ast_map) = driver::phase_2_configure_and_expand(sess, + cfg.clone(), + &mut loader, + crate); + let CrateLoader { mut installer, .. } = loader; + debug!("About to call find_and_install_dependencies..."); + find_and_install_dependencies(&mut installer, &crate); + (crate, ast_map) + }; // Inject the crate_id attribute so we get the right package name and version if !attr::contains_name(crate.attrs, "crate_id") { @@ -430,19 +451,18 @@ pub fn compile_crate(ctxt: &BuildContext, compile_input(ctxt, exec, pkg_id, crate, workspace, deps, flags, cfgs, opt, what) } -struct ViewItemVisitor<'a> { +struct CrateInstaller<'a> { context: &'a BuildContext, parent: &'a CrateId, parent_crate: &'a Path, sess: session::Session, exec: &'a mut workcache::Exec, - c: &'a ast::Crate, save: 'a |Path|, deps: &'a mut DepMap } -impl<'a> Visitor<()> for ViewItemVisitor<'a> { - fn visit_view_item(&mut self, vi: &ast::ViewItem, env: ()) { +impl<'a> CrateInstaller<'a> { + fn install_crate(&mut self, vi: &ast::ViewItem) { use conditions::nonexistent_package::cond; match vi.node { @@ -585,33 +605,43 @@ impl<'a> Visitor<()> for ViewItemVisitor<'a> { // Ignore `use`s _ => () } + } +} + +impl<'a> Visitor<()> for CrateInstaller<'a> { + fn visit_view_item(&mut self, vi: &ast::ViewItem, env: ()) { + self.install_crate(vi); visit::walk_view_item(self, vi, env) } } +struct CrateLoader<'a> { + installer: CrateInstaller<'a>, + loader: Loader, +} + +impl<'a> base::CrateLoader for CrateLoader<'a> { + fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate { + self.installer.install_crate(crate); + self.loader.load_crate(crate) + } + + fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> ~[@ast::Item] { + self.loader.get_exported_macros(cnum) + } + + fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> { + self.loader.get_registrar_symbol(cnum) + } +} + /// Collect all `extern mod` directives in `c`, then /// try to install their targets, failing if any target /// can't be found. -pub fn find_and_install_dependencies(context: &BuildContext, - parent: &CrateId, - parent_crate: &Path, - sess: session::Session, - exec: &mut workcache::Exec, - c: &ast::Crate, - deps: &mut DepMap, - save: |Path|) { +pub fn find_and_install_dependencies(installer: &mut CrateInstaller, + c: &ast::Crate) { debug!("In find_and_install_dependencies..."); - let mut visitor = ViewItemVisitor { - context: context, - parent: parent, - parent_crate: parent_crate, - sess: sess, - exec: exec, - c: c, - save: save, - deps: deps - }; - visit::walk_crate(&mut visitor, c, ()) + visit::walk_crate(installer, c, ()) } pub fn mk_string_lit(s: @str) -> ast::Lit { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 8515c3aba50..ef6d154c651 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -20,6 +20,7 @@ use parse::token::{ident_to_str, intern, str_to_ident}; use util::small_vector::SmallVector; use std::hashmap::HashMap; +use std::unstable::dynamic_lib::DynamicLibrary; // new-style macro! tt code: // @@ -120,6 +121,9 @@ pub type SyntaxExpanderTTItemFun = pub type SyntaxExpanderTTItemFunNoCtxt = fn(&mut ExtCtxt, Span, ast::Ident, ~[ast::TokenTree]) -> MacResult; +pub type MacroCrateRegistrationFun = + extern "Rust" fn(|ast::Name, SyntaxExtension|); + pub trait AnyMacro { fn make_expr(&self) -> @ast::Expr; fn make_items(&self) -> SmallVector<@ast::Item>; @@ -151,24 +155,21 @@ pub enum SyntaxExtension { IdentTT(~SyntaxExpanderTTItemTrait:'static, Option), } - -// The SyntaxEnv is the environment that's threaded through the expansion -// of macros. It contains bindings for macros, and also a special binding -// for " block" (not a legal identifier) that maps to a BlockInfo -pub type SyntaxEnv = MapChain; - pub struct BlockInfo { // should macros escape from this scope? macros_escape : bool, // what are the pending renames? - pending_renames : RenameList + pending_renames : RenameList, + // references for crates loaded in this scope + macro_crates: ~[DynamicLibrary], } impl BlockInfo { pub fn new() -> BlockInfo { BlockInfo { macros_escape: false, - pending_renames: ~[] + pending_renames: ~[], + macro_crates: ~[], } } } @@ -189,7 +190,7 @@ pub fn syntax_expander_table() -> SyntaxEnv { None) } - let mut syntax_expanders = MapChain::new(); + let mut syntax_expanders = SyntaxEnv::new(); syntax_expanders.insert(intern(&"macro_rules"), IdentTT(~SyntaxExpanderTTItem { expander: SyntaxExpanderTTItemExpanderWithContext( @@ -280,25 +281,38 @@ pub fn syntax_expander_table() -> SyntaxEnv { syntax_expanders } +pub struct MacroCrate { + lib: Option, + cnum: ast::CrateNum, +} + +pub trait CrateLoader { + fn load_crate(&mut self, crate: &ast::ViewItem) -> MacroCrate; + fn get_exported_macros(&mut self, crate_num: ast::CrateNum) -> ~[@ast::Item]; + fn get_registrar_symbol(&mut self, crate_num: ast::CrateNum) -> Option<~str>; +} + // One of these is made during expansion and incrementally updated as we go; // when a macro expansion occurs, the resulting nodes have the backtrace() // -> expn_info of their expansion context stored into their span. -pub struct ExtCtxt { +pub struct ExtCtxt<'a> { parse_sess: @parse::ParseSess, cfg: ast::CrateConfig, backtrace: Option<@ExpnInfo>, + loader: &'a mut CrateLoader, mod_path: ~[ast::Ident], trace_mac: bool } -impl ExtCtxt { - pub fn new(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig) - -> ExtCtxt { +impl<'a> ExtCtxt<'a> { + pub fn new<'a>(parse_sess: @parse::ParseSess, cfg: ast::CrateConfig, + loader: &'a mut CrateLoader) -> ExtCtxt<'a> { ExtCtxt { parse_sess: parse_sess, cfg: cfg, backtrace: None, + loader: loader, mod_path: ~[], trace_mac: false } @@ -456,20 +470,27 @@ pub fn get_exprs_from_tts(cx: &ExtCtxt, // able to refer to a macro that was added to an enclosing // scope lexically later than the deeper scope. -// Only generic to make it easy to test -struct MapChainFrame { +struct MapChainFrame { info: BlockInfo, - map: HashMap, + map: HashMap, +} + +#[unsafe_destructor] +impl Drop for MapChainFrame { + fn drop(&mut self) { + // make sure that syntax extension dtors run before we drop the libs + self.map.clear(); + } } // Only generic to make it easy to test -pub struct MapChain { - priv chain: ~[MapChainFrame], +pub struct SyntaxEnv { + priv chain: ~[MapChainFrame], } -impl MapChain { - pub fn new() -> MapChain { - let mut map = MapChain { chain: ~[] }; +impl SyntaxEnv { + pub fn new() -> SyntaxEnv { + let mut map = SyntaxEnv { chain: ~[] }; map.push_frame(); map } @@ -486,7 +507,7 @@ impl MapChain { self.chain.pop(); } - fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame { + fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame { for (i, frame) in self.chain.mut_iter().enumerate().invert() { if !frame.info.macros_escape || i == 0 { return frame @@ -495,7 +516,7 @@ impl MapChain { unreachable!() } - pub fn find<'a>(&'a self, k: &K) -> Option<&'a V> { + pub fn find<'a>(&'a self, k: &Name) -> Option<&'a SyntaxExtension> { for frame in self.chain.iter().invert() { match frame.map.find(k) { Some(v) => return Some(v), @@ -505,49 +526,15 @@ impl MapChain { None } - pub fn insert(&mut self, k: K, v: V) { + pub fn insert(&mut self, k: Name, v: SyntaxExtension) { self.find_escape_frame().map.insert(k, v); } + pub fn insert_macro_crate(&mut self, lib: DynamicLibrary) { + self.find_escape_frame().info.macro_crates.push(lib); + } + pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo { &mut self.chain[self.chain.len()-1].info } } - -#[cfg(test)] -mod test { - use super::MapChain; - - #[test] - fn testenv() { - let mut m = MapChain::new(); - let (a,b,c,d) = ("a", "b", "c", "d"); - m.insert(1, a); - assert_eq!(Some(&a), m.find(&1)); - - m.push_frame(); - m.info().macros_escape = true; - m.insert(2, b); - assert_eq!(Some(&a), m.find(&1)); - assert_eq!(Some(&b), m.find(&2)); - m.pop_frame(); - - assert_eq!(Some(&a), m.find(&1)); - assert_eq!(Some(&b), m.find(&2)); - - m.push_frame(); - m.push_frame(); - m.info().macros_escape = true; - m.insert(3, c); - assert_eq!(Some(&c), m.find(&3)); - m.pop_frame(); - assert_eq!(Some(&c), m.find(&3)); - m.pop_frame(); - assert_eq!(None, m.find(&3)); - - m.push_frame(); - m.insert(4, d); - m.pop_frame(); - assert_eq!(None, m.find(&4)); - } -} diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 85cda0bd1ae..d702ee3ead4 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -236,7 +236,7 @@ pub trait AstBuilder { vis: ast::Visibility, path: ~[ast::Ident]) -> ast::ViewItem; } -impl AstBuilder for ExtCtxt { +impl<'a> AstBuilder for ExtCtxt<'a> { fn path(&self, span: Span, strs: ~[ast::Ident]) -> ast::Path { self.path_all(span, false, strs, opt_vec::Empty, ~[]) } @@ -896,7 +896,7 @@ impl AstBuilder for ExtCtxt { } struct Duplicator<'a> { - cx: &'a ExtCtxt, + cx: &'a ExtCtxt<'a>, } impl<'a> Folder for Duplicator<'a> { diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 826d5381d47..202746acddc 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -190,7 +190,7 @@ mod ty; pub struct TraitDef<'a> { /// The extension context - cx: &'a ExtCtxt, + cx: &'a ExtCtxt<'a>, /// The span for the current #[deriving(Foo)] header. span: Span, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 303277afbe8..b1b38d6dc90 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -30,6 +30,7 @@ use visit::Visitor; use util::small_vector::SmallVector; use std::vec; +use std::unstable::dynamic_lib::DynamicLibrary; pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { match e.node { @@ -365,13 +366,79 @@ pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander) // yikes... no idea how to apply the mark to this. I'm afraid // we're going to have to wait-and-see on this one. fld.extsbox.insert(intern(name), ext); - SmallVector::zero() + if attr::contains_name(it.attrs, "macro_export") { + SmallVector::one(it) + } else { + SmallVector::zero() + } } }; fld.cx.bt_pop(); return items; } +// load macros from syntax-phase crates +pub fn expand_view_item(vi: &ast::ViewItem, + fld: &mut MacroExpander) + -> ast::ViewItem { + let should_load = vi.attrs.iter().any(|attr| { + "phase" == attr.name() && + attr.meta_item_list().map_or(false, |phases| { + attr::contains_name(phases, "syntax") + }) + }); + + if should_load { + load_extern_macros(vi, fld); + } + + noop_fold_view_item(vi, fld) +} + +fn load_extern_macros(crate: &ast::ViewItem, fld: &mut MacroExpander) { + let MacroCrate { lib, cnum } = fld.cx.loader.load_crate(crate); + + let exported_macros = fld.cx.loader.get_exported_macros(cnum); + for &it in exported_macros.iter() { + expand_item_mac(it, fld); + } + + let path = match lib { + Some(path) => path, + None => return + }; + // Make sure the path contains a / or the linker will search for it. + // If path is already absolute this is a no-op. + let path = Path::new(".").join(path); + + let registrar = match fld.cx.loader.get_registrar_symbol(cnum) { + Some(registrar) => registrar, + None => return + }; + + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(err) => fld.cx.span_fatal(crate.span, err) + }; + + unsafe { + let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) { + Ok(registrar) => registrar, + Err(err) => fld.cx.span_fatal(crate.span, err) + }; + registrar(|name, extension| { + let extension = match extension { + NormalTT(ext, _) => NormalTT(ext, Some(crate.span)), + IdentTT(ext, _) => IdentTT(ext, Some(crate.span)), + ItemDecorator(ext) => ItemDecorator(ext), + }; + fld.extsbox.insert(name, extension); + }); + } + + fld.extsbox.insert_macro_crate(lib); +} + // expand a stmt pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> { // why the copying here and not in expand_expr? @@ -878,7 +945,7 @@ pub fn inject_std_macros(parse_sess: @parse::ParseSess, pub struct MacroExpander<'a> { extsbox: SyntaxEnv, - cx: &'a mut ExtCtxt, + cx: &'a mut ExtCtxt<'a>, } impl<'a> Folder for MacroExpander<'a> { @@ -894,6 +961,10 @@ impl<'a> Folder for MacroExpander<'a> { expand_item(item, self) } + fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem { + expand_view_item(vi, self) + } + fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> { expand_stmt(stmt, self) } @@ -908,9 +979,10 @@ impl<'a> Folder for MacroExpander<'a> { } pub fn expand_crate(parse_sess: @parse::ParseSess, + loader: &mut CrateLoader, cfg: ast::CrateConfig, c: Crate) -> Crate { - let mut cx = ExtCtxt::new(parse_sess, cfg.clone()); + let mut cx = ExtCtxt::new(parse_sess, cfg.clone(), loader); let mut expander = MacroExpander { extsbox: syntax_expander_table(), cx: &mut cx, @@ -1076,6 +1148,7 @@ mod test { use codemap::Spanned; use fold; use fold::*; + use ext::base::{CrateLoader, MacroCrate}; use parse; use parse::token::{fresh_mark, gensym, intern, ident_to_str}; use parse::token; @@ -1119,6 +1192,22 @@ mod test { } } + struct ErrLoader; + + impl CrateLoader for ErrLoader { + fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate { + fail!("lolwut") + } + + fn get_exported_macros(&mut self, _: ast::CrateNum) -> ~[@ast::Item] { + fail!("lolwut") + } + + fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> { + fail!("lolwut") + } + } + // make sure that fail! is present #[test] fn fail_exists_test () { let src = @"fn main() { fail!(\"something appropriately gloomy\");}"; @@ -1129,7 +1218,8 @@ mod test { ~[],sess); let crate_ast = inject_std_macros(sess, ~[], crate_ast); // don't bother with striping, doesn't affect fail!. - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } // these following tests are quite fragile, in that they don't test what @@ -1146,7 +1236,8 @@ mod test { src, ~[],sess); // should fail: - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } // make sure that macros can leave scope for modules @@ -1160,7 +1251,8 @@ mod test { src, ~[],sess); // should fail: - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } // macro_escape modules shouldn't cause macros to leave scope @@ -1173,7 +1265,8 @@ mod test { src, ~[], sess); // should fail: - expand_crate(sess,~[],crate_ast); + let mut loader = ErrLoader; + expand_crate(sess,&mut loader,~[],crate_ast); } #[test] fn std_macros_must_parse () { @@ -1281,7 +1374,8 @@ mod test { fn expand_crate_str(crate_str: @str) -> ast::Crate { let (crate_ast,ps) = string_to_crate_and_sess(crate_str); // the cfg argument actually does matter, here... - expand_crate(ps,~[],crate_ast) + let mut loader = ErrLoader; + expand_crate(ps,&mut loader,~[],crate_ast) } //fn expand_and_resolve(crate_str: @str) -> ast::crate { diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index a4b8dd78403..9ae13ddeb02 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -34,7 +34,7 @@ enum Position { } struct Context<'a> { - ecx: &'a mut ExtCtxt, + ecx: &'a mut ExtCtxt<'a>, fmtsp: Span, // Parsed argument expressions and the types that we've found so far for diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index e66e394d639..27f41356a4b 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -243,7 +243,7 @@ pub mod rt { fn parse_tts(&self, s: @str) -> ~[ast::TokenTree]; } - impl ExtParseUtils for ExtCtxt { + impl<'a> ExtParseUtils for ExtCtxt<'a> { fn parse_item(&self, s: @str) -> @ast::Item { let res = parse::parse_item_from_source_str( diff --git a/src/libsyntax/ext/registrar.rs b/src/libsyntax/ext/registrar.rs new file mode 100644 index 00000000000..265dd91d7f4 --- /dev/null +++ b/src/libsyntax/ext/registrar.rs @@ -0,0 +1,60 @@ +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast; +use attr; +use codemap::Span; +use diagnostic; +use visit; +use visit::Visitor; + +struct MacroRegistrarContext { + registrars: ~[(ast::NodeId, Span)], +} + +impl Visitor<()> for MacroRegistrarContext { + fn visit_item(&mut self, item: &ast::Item, _: ()) { + match item.node { + ast::ItemFn(..) => { + if attr::contains_name(item.attrs, "macro_registrar") { + self.registrars.push((item.id, item.span)); + } + } + _ => {} + } + + visit::walk_item(self, item, ()); + } +} + +pub fn find_macro_registrar(diagnostic: @diagnostic::SpanHandler, + crate: &ast::Crate) -> Option { + let mut ctx = MacroRegistrarContext { registrars: ~[] }; + visit::walk_crate(&mut ctx, crate, ()); + + match ctx.registrars.len() { + 0 => None, + 1 => { + let (node_id, _) = ctx.registrars.pop(); + Some(ast::DefId { + crate: ast::LOCAL_CRATE, + node: node_id + }) + }, + _ => { + diagnostic.handler().err("Multiple macro registration functions found"); + for &(_, span) in ctx.registrars.iter() { + diagnostic.span_note(span, "one is here"); + } + diagnostic.handler().abort_if_errors(); + unreachable!(); + } + } +} diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 35b29783b67..7a86dd6e4ce 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -63,20 +63,7 @@ pub trait Folder { } fn fold_view_item(&mut self, vi: &ViewItem) -> ViewItem { - let inner_view_item = match vi.node { - ViewItemExternMod(ref ident, string, node_id) => { - ViewItemExternMod(ident.clone(), string, self.new_id(node_id)) - } - ViewItemUse(ref view_paths) => { - ViewItemUse(self.fold_view_paths(*view_paths)) - } - }; - ViewItem { - node: inner_view_item, - attrs: vi.attrs.map(|a| fold_attribute_(*a, self)), - vis: vi.vis, - span: self.new_span(vi.span), - } + noop_fold_view_item(vi, self) } fn fold_foreign_item(&mut self, ni: @ForeignItem) -> @ForeignItem { @@ -509,6 +496,28 @@ fn fold_variant_arg_(va: &VariantArg, folder: &mut T) -> VariantArg { } } +pub fn noop_fold_view_item(vi: &ViewItem, folder: &mut T) + -> ViewItem{ + let inner_view_item = match vi.node { + ViewItemExternMod(ref ident, + string, + node_id) => { + ViewItemExternMod(ident.clone(), + string, + folder.new_id(node_id)) + } + ViewItemUse(ref view_paths) => { + ViewItemUse(folder.fold_view_paths(*view_paths)) + } + }; + ViewItem { + node: inner_view_item, + attrs: vi.attrs.map(|a| fold_attribute_(*a, folder)), + vis: vi.vis, + span: folder.new_span(vi.span), + } +} + pub fn noop_fold_block(b: P, folder: &mut T) -> P { let view_items = b.view_items.map(|x| folder.fold_view_item(x)); let stmts = b.stmts.iter().flat_map(|s| folder.fold_stmt(*s).move_iter()).collect(); diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 7eebbc75f94..532a2a9a314 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -68,6 +68,7 @@ pub mod ext { pub mod asm; pub mod base; pub mod expand; + pub mod registrar; pub mod quote; diff --git a/src/test/auxiliary/macro_crate_def_only.rs b/src/test/auxiliary/macro_crate_def_only.rs new file mode 100644 index 00000000000..145212bcc8c --- /dev/null +++ b/src/test/auxiliary/macro_crate_def_only.rs @@ -0,0 +1,16 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(macro_rules)]; + +#[macro_export] +macro_rules! make_a_5( + () => (5) +) diff --git a/src/test/auxiliary/macro_crate_test.rs b/src/test/auxiliary/macro_crate_test.rs new file mode 100644 index 00000000000..403bbac8eb2 --- /dev/null +++ b/src/test/auxiliary/macro_crate_test.rs @@ -0,0 +1,42 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(globs, macro_registrar, macro_rules)]; + +extern mod syntax; + +use syntax::ast::{Name, TokenTree}; +use syntax::codemap::Span; +use syntax::ext::base::*; +use syntax::parse::token; + +#[macro_export] +macro_rules! exported_macro (() => (2)) + +macro_rules! unexported_macro (() => (3)) + +#[macro_registrar] +pub fn macro_registrar(register: |Name, SyntaxExtension|) { + register(token::intern("make_a_1"), + NormalTT(~SyntaxExpanderTT { + expander: SyntaxExpanderTTExpanderWithoutContext(expand_make_a_1), + span: None, + }, + None)); +} + +pub fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> MacResult { + if !tts.is_empty() { + cx.span_fatal(sp, "make_a_1 takes no arguments"); + } + MRExpr(quote_expr!(cx, 1i)) +} + +pub fn foo() {} diff --git a/src/test/compile-fail/gated-macro_registrar.rs b/src/test/compile-fail/gated-macro_registrar.rs new file mode 100644 index 00000000000..54274ccb847 --- /dev/null +++ b/src/test/compile-fail/gated-macro_registrar.rs @@ -0,0 +1,15 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// the registration function isn't typechecked yet +#[macro_registrar] +pub fn registrar() {} //~ ERROR cross-crate macro exports are experimental + +fn main() {} diff --git a/src/test/compile-fail/gated-phase.rs b/src/test/compile-fail/gated-phase.rs new file mode 100644 index 00000000000..3a801e1351a --- /dev/null +++ b/src/test/compile-fail/gated-phase.rs @@ -0,0 +1,17 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_crate_test.rs + +#[phase(syntax)] +//~^ ERROR compile time crate loading is experimental and possibly buggy +extern mod macro_crate_test; + +fn main() {} diff --git a/src/test/compile-fail/macro-crate-unexported-macro.rs b/src/test/compile-fail/macro-crate-unexported-macro.rs new file mode 100644 index 00000000000..d6867e780ef --- /dev/null +++ b/src/test/compile-fail/macro-crate-unexported-macro.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_crate_test.rs +// xfail-stage1 + +#[feature(phase)]; + +#[phase(syntax)] +extern mod macro_crate_test; + +fn main() { + assert_eq!(3, unexported_macro!()); //~ ERROR macro undefined: 'unexported_macro' +} diff --git a/src/test/compile-fail/macro-crate-unknown-crate.rs b/src/test/compile-fail/macro-crate-unknown-crate.rs new file mode 100644 index 00000000000..83538fcf454 --- /dev/null +++ b/src/test/compile-fail/macro-crate-unknown-crate.rs @@ -0,0 +1,16 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[feature(phase)]; + +#[phase(syntax)] +extern mod doesnt_exist; //~ ERROR can't find crate + +fn main() {} diff --git a/src/test/compile-fail/multiple-macro-registrars.rs b/src/test/compile-fail/multiple-macro-registrars.rs new file mode 100644 index 00000000000..7802c13bd3a --- /dev/null +++ b/src/test/compile-fail/multiple-macro-registrars.rs @@ -0,0 +1,22 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern: Multiple macro registration functions found + +#[feature(macro_registrar)]; + +// the registration function isn't typechecked yet +#[macro_registrar] +pub fn one() {} + +#[macro_registrar] +pub fn two() {} + +fn main() {} diff --git a/src/test/compile-fail/phase-syntax-doesnt-resolve.rs b/src/test/compile-fail/phase-syntax-doesnt-resolve.rs new file mode 100644 index 00000000000..c7e49d2dd60 --- /dev/null +++ b/src/test/compile-fail/phase-syntax-doesnt-resolve.rs @@ -0,0 +1,24 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_crate_test.rs +// xfail-stage1 + +#[feature(phase)]; + +#[phase(syntax)] +extern mod macro_crate_test; + +fn main() { + macro_crate_test::foo(); + //~^ ERROR unresolved name + //~^^ ERROR use of undeclared module `macro_crate_test` + //~^^^ ERROR unresolved name `macro_crate_test::foo`. +} diff --git a/src/test/run-pass/macro-crate-def-only.rs b/src/test/run-pass/macro-crate-def-only.rs new file mode 100644 index 00000000000..b5ae00e1d7b --- /dev/null +++ b/src/test/run-pass/macro-crate-def-only.rs @@ -0,0 +1,21 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_crate_def_only.rs +// xfail-fast + +#[feature(phase)]; + +#[phase(syntax)] +extern mod macro_crate_def_only; + +pub fn main() { + assert_eq!(5, make_a_5!()); +} diff --git a/src/test/run-pass/macro-crate.rs b/src/test/run-pass/macro-crate.rs new file mode 100644 index 00000000000..0073c1c33fb --- /dev/null +++ b/src/test/run-pass/macro-crate.rs @@ -0,0 +1,23 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_crate_test.rs +// xfail-stage1 +// xfail-fast + +#[feature(phase)]; + +#[phase(syntax)] +extern mod macro_crate_test; + +pub fn main() { + assert_eq!(1, make_a_1!()); + assert_eq!(2, exported_macro!()); +} diff --git a/src/test/run-pass/phase-syntax-link-does-resolve.rs b/src/test/run-pass/phase-syntax-link-does-resolve.rs new file mode 100644 index 00000000000..20c6fa5efaa --- /dev/null +++ b/src/test/run-pass/phase-syntax-link-does-resolve.rs @@ -0,0 +1,23 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:macro_crate_test.rs +// xfail-stage1 +// xfail-fast + +#[feature(phase)]; + +#[phase(syntax, link)] +extern mod macro_crate_test; + +fn main() { + assert_eq!(1, make_a_1!()); + macro_crate_test::foo(); +}