From 86a8b031f5eaa80ed9b1f789bd857257cdd7122c Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 23 Jan 2014 23:40:54 -0800 Subject: [PATCH] Move macro_rules! macros to libstd They all have to go into a single module at the moment unfortunately. Ideally, the logging macros would live in std::logging, condition! would live in std::condition, format! in std::fmt, etc. However, this introduces cyclic dependencies between those modules and the macros they use which the current expansion system can't deal with. We may be able to get around this by changing the expansion phase to a two-pass system but that's for a later PR. Closes #2247 cc #11763 --- src/librustc/driver/driver.rs | 11 +- src/librustc/front/std_inject.rs | 66 +++-- src/libstd/lib.rs | 4 +- src/libstd/macros.rs | 192 ++++++++++++++ src/libsyntax/ext/expand.rs | 241 ------------------ .../tag-that-dare-not-speak-its-name.rs | 4 +- 6 files changed, 247 insertions(+), 271 deletions(-) create mode 100644 src/libstd/macros.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 301a49d8aec66..9e11e8f9b370a 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -176,6 +176,9 @@ pub fn phase_2_configure_and_expand(sess: Session, time(time_passes, "gated feature checking", (), |_| front::feature_gate::check_crate(sess, &crate)); + crate = time(time_passes, "crate injection", crate, |crate| + front::std_inject::maybe_inject_crates_ref(sess, crate)); + // strip before expansion to allow macros to depend on // configuration variables e.g/ in // @@ -183,10 +186,6 @@ pub fn phase_2_configure_and_expand(sess: Session, // mod bar { macro_rules! baz!(() => {{}}) } // // baz! should not use this definition unless foo is enabled. - crate = time(time_passes, "std macros injection", crate, |crate| - syntax::ext::expand::inject_std_macros(sess.parse_sess, - cfg.clone(), - crate)); crate = time(time_passes, "configuration 1", crate, |crate| front::config::strip_unconfigured_items(crate)); @@ -207,8 +206,8 @@ pub fn phase_2_configure_and_expand(sess: Session, crate = time(time_passes, "maybe building test harness", crate, |crate| front::test::modify_for_testing(sess, crate)); - crate = time(time_passes, "std injection", crate, |crate| - front::std_inject::maybe_inject_libstd_ref(sess, crate)); + crate = time(time_passes, "prelude injection", crate, |crate| + front::std_inject::maybe_inject_prelude(sess, crate)); time(time_passes, "assinging node ids and indexing ast", crate, |crate| front::assign_node_ids_and_map::assign_node_ids_and_map(sess, crate)) diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index 6fa54061e3440..71a82536aee0c 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -23,10 +23,18 @@ use syntax::util::small_vector::SmallVector; pub static VERSION: &'static str = "0.10-pre"; -pub fn maybe_inject_libstd_ref(sess: Session, crate: ast::Crate) +pub fn maybe_inject_crates_ref(sess: Session, crate: ast::Crate) -> ast::Crate { if use_std(&crate) { - inject_libstd_ref(sess, crate) + inject_crates_ref(sess, crate) + } else { + crate + } +} + +pub fn maybe_inject_prelude(sess: Session, crate: ast::Crate) -> ast::Crate { + if use_std(&crate) { + inject_prelude(sess, crate) } else { crate } @@ -44,13 +52,6 @@ fn no_prelude(attrs: &[ast::Attribute]) -> bool { attr::contains_name(attrs, "no_implicit_prelude") } -fn spanned(x: T) -> codemap::Spanned { - codemap::Spanned { - node: x, - span: DUMMY_SP, - } -} - struct StandardLibraryInjector { sess: Session, } @@ -71,7 +72,11 @@ impl fold::Folder for StandardLibraryInjector { node: ast::ViewItemExternMod(self.sess.ident_of("std"), with_version("std"), ast::DUMMY_NODE_ID), - attrs: ~[], + attrs: ~[ + attr::mk_attr(attr::mk_list_item(@"phase", + ~[attr::mk_word_item(@"syntax"), + attr::mk_word_item(@"link")])) + ], vis: ast::Inherited, span: DUMMY_SP }]; @@ -96,22 +101,43 @@ impl fold::Folder for StandardLibraryInjector { } vis.push_all(crate.module.view_items); - let mut new_module = ast::Mod { + let new_module = ast::Mod { view_items: vis, ..crate.module.clone() }; - if !no_prelude(crate.attrs) { - // only add `use std::prelude::*;` if there wasn't a - // `#[no_implicit_prelude];` at the crate level. - new_module = self.fold_mod(&new_module); - } - ast::Crate { module: new_module, ..crate } } +} + +fn inject_crates_ref(sess: Session, crate: ast::Crate) -> ast::Crate { + let mut fold = StandardLibraryInjector { + sess: sess, + }; + fold.fold_crate(crate) +} + +struct PreludeInjector { + sess: Session, +} + + +impl fold::Folder for PreludeInjector { + fn fold_crate(&mut self, crate: ast::Crate) -> ast::Crate { + if !no_prelude(crate.attrs) { + // only add `use std::prelude::*;` if there wasn't a + // `#[no_implicit_prelude];` at the crate level. + ast::Crate { + module: self.fold_mod(&crate.module), + ..crate + } + } else { + crate + } + } fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> { if !no_prelude(item.attrs) { @@ -142,7 +168,7 @@ impl fold::Folder for StandardLibraryInjector { ], }; - let vp = @spanned(ast::ViewPathGlob(prelude_path, ast::DUMMY_NODE_ID)); + let vp = @codemap::dummy_spanned(ast::ViewPathGlob(prelude_path, ast::DUMMY_NODE_ID)); let vi2 = ast::ViewItem { node: ast::ViewItemUse(~[vp]), attrs: ~[], @@ -161,8 +187,8 @@ impl fold::Folder for StandardLibraryInjector { } } -fn inject_libstd_ref(sess: Session, crate: ast::Crate) -> ast::Crate { - let mut fold = StandardLibraryInjector { +fn inject_prelude(sess: Session, crate: ast::Crate) -> ast::Crate { + let mut fold = PreludeInjector { sess: sess, }; fold.fold_crate(crate) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7e53a0071bd1c..4f13a51787871 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -76,6 +76,8 @@ #[cfg(test)] pub use ops = realstd::ops; #[cfg(test)] pub use cmp = realstd::cmp; +mod macros; + mod rtdeps; /* The Prelude. */ diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs new file mode 100644 index 0000000000000..effc9a77c1d71 --- /dev/null +++ b/src/libstd/macros.rs @@ -0,0 +1,192 @@ +// 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. +#[macro_escape]; + +#[macro_export] +macro_rules! ignore (($($x:tt)*) => (())) + +#[macro_export] +macro_rules! log( + ($lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= __log_level() { + format_args!(|args| { + ::std::logging::log(lvl, args) + }, $($arg)+) + } + }) +) +#[macro_export] +macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) ) +#[macro_export] +macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) ) +#[macro_export] +macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) ) +#[macro_export] +macro_rules! debug( ($($arg:tt)*) => ( + if cfg!(not(ndebug)) { log!(4u32, $($arg)*) } +)) + +#[macro_export] +macro_rules! log_enabled( + ($lvl:expr) => ( { + let lvl = $lvl; + lvl <= __log_level() && (lvl != 4 || cfg!(not(ndebug))) + } ) +) + +#[macro_export] +macro_rules! fail( + () => ( + fail!("explicit failure") + ); + ($msg:expr) => ( + ::std::rt::begin_unwind($msg, file!(), line!()) + ); + ($fmt:expr, $($arg:tt)*) => ( + ::std::rt::begin_unwind(format!($fmt, $($arg)*), file!(), line!()) + ) +) + +#[macro_export] +macro_rules! assert( + ($cond:expr) => { + if !$cond { + fail!("assertion failed: {:s}", stringify!($cond)) + } + }; + ($cond:expr, $msg:expr) => { + if !$cond { + fail!($msg) + } + }; + ($cond:expr, $( $arg:expr ),+) => { + if !$cond { + fail!( $($arg),+ ) + } + } +) + +#[macro_export] +macro_rules! assert_eq ( + ($given:expr , $expected:expr) => ( + { + let given_val = &($given); + let expected_val = &($expected); + // check both directions of equality.... + if !((*given_val == *expected_val) && + (*expected_val == *given_val)) { + fail!("assertion failed: `(left == right) && (right == left)` \ + (left: `{:?}`, right: `{:?}`)", *given_val, *expected_val) + } + } + ) +) + +/// A utility macro for indicating unreachable code. It will fail if +/// executed. This is occasionally useful to put after loops that never +/// terminate normally, but instead directly return from a function. +/// +/// # Example +/// +/// ```rust +/// fn choose_weighted_item(v: &[Item]) -> Item { +/// assert!(!v.is_empty()); +/// let mut so_far = 0u; +/// for item in v.iter() { +/// so_far += item.weight; +/// if so_far > 100 { +/// return item; +/// } +/// } +/// // The above loop always returns, so we must hint to the +/// // type checker that it isn't possible to get down here +/// unreachable!(); +/// } +/// ``` +#[macro_export] +macro_rules! unreachable (() => ( + fail!("internal error: entered unreachable code"); +)) + +#[macro_export] +macro_rules! condition ( + + { pub $c:ident: $input:ty -> $out:ty; } => { + + pub mod $c { + #[allow(unused_imports)]; + #[allow(non_uppercase_statics)]; + #[allow(missing_doc)]; + + use super::*; + + local_data_key!(key: @::std::condition::Handler<$input, $out>) + + pub static cond : + ::std::condition::Condition<$input,$out> = + ::std::condition::Condition { + name: stringify!($c), + key: key + }; + } + }; + + { $c:ident: $input:ty -> $out:ty; } => { + + mod $c { + #[allow(unused_imports)]; + #[allow(non_uppercase_statics)]; + #[allow(dead_code)]; + + use super::*; + + local_data_key!(key: @::std::condition::Handler<$input, $out>) + + pub static cond : + ::std::condition::Condition<$input,$out> = + ::std::condition::Condition { + name: stringify!($c), + key: key + }; + } + } +) + +#[macro_export] +macro_rules! format(($($arg:tt)*) => ( + format_args!(::std::fmt::format, $($arg)*) +)) +#[macro_export] +macro_rules! write(($dst:expr, $($arg:tt)*) => ( + format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*) +)) +#[macro_export] +macro_rules! writeln(($dst:expr, $($arg:tt)*) => ( + format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*) +)) +#[macro_export] +macro_rules! print ( + ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*)) +) +#[macro_export] +macro_rules! println ( + ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*)) +) + +#[macro_export] +macro_rules! local_data_key ( + ($name:ident: $ty:ty) => ( + static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key; + ); + (pub $name:ident: $ty:ty) => ( + pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key; + ) +) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0534aa39848b8..43a5b01698b4e 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -22,7 +22,6 @@ use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use ext::base::*; use fold::*; use parse; -use parse::{parse_item_from_source_str}; use parse::token; use parse::token::{fresh_mark, fresh_name, ident_to_str, intern}; use visit; @@ -754,218 +753,6 @@ pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span { } } -// FIXME (#2247): this is a moderately bad kludge to inject some macros into -// the default compilation environment in that it injects strings, rather than -// syntax elements. - -pub fn std_macros() -> @str { -@r#"mod __std_macros { - #[macro_escape]; - #[doc(hidden)]; - #[allow(dead_code)]; - - macro_rules! ignore (($($x:tt)*) => (())) - - macro_rules! log( - ($lvl:expr, $($arg:tt)+) => ({ - let lvl = $lvl; - if lvl <= __log_level() { - format_args!(|args| { - ::std::logging::log(lvl, args) - }, $($arg)+) - } - }) - ) - macro_rules! error( ($($arg:tt)*) => (log!(1u32, $($arg)*)) ) - macro_rules! warn ( ($($arg:tt)*) => (log!(2u32, $($arg)*)) ) - macro_rules! info ( ($($arg:tt)*) => (log!(3u32, $($arg)*)) ) - macro_rules! debug( ($($arg:tt)*) => ( - if cfg!(not(ndebug)) { log!(4u32, $($arg)*) } - )) - - macro_rules! log_enabled( - ($lvl:expr) => ( { - let lvl = $lvl; - lvl <= __log_level() && (lvl != 4 || cfg!(not(ndebug))) - } ) - ) - - macro_rules! fail( - () => ( - fail!("explicit failure") - ); - ($msg:expr) => ( - ::std::rt::begin_unwind($msg, file!(), line!()) - ); - ($fmt:expr, $($arg:tt)*) => ( - ::std::rt::begin_unwind(format!($fmt, $($arg)*), file!(), line!()) - ) - ) - - macro_rules! assert( - ($cond:expr) => { - if !$cond { - fail!("assertion failed: {:s}", stringify!($cond)) - } - }; - ($cond:expr, $msg:expr) => { - if !$cond { - fail!($msg) - } - }; - ($cond:expr, $( $arg:expr ),+) => { - if !$cond { - fail!( $($arg),+ ) - } - } - ) - - macro_rules! assert_eq ( - ($given:expr , $expected:expr) => ( - { - let given_val = &($given); - let expected_val = &($expected); - // check both directions of equality.... - if !((*given_val == *expected_val) && - (*expected_val == *given_val)) { - fail!("assertion failed: `(left == right) && (right == left)` \ - (left: `{:?}`, right: `{:?}`)", *given_val, *expected_val) - } - } - ) - ) - - /// A utility macro for indicating unreachable code. It will fail if - /// executed. This is occasionally useful to put after loops that never - /// terminate normally, but instead directly return from a function. - /// - /// # Example - /// - /// ```rust - /// fn choose_weighted_item(v: &[Item]) -> Item { - /// assert!(!v.is_empty()); - /// let mut so_far = 0u; - /// for item in v.iter() { - /// so_far += item.weight; - /// if so_far > 100 { - /// return item; - /// } - /// } - /// // The above loop always returns, so we must hint to the - /// // type checker that it isn't possible to get down here - /// unreachable!(); - /// } - /// ``` - macro_rules! unreachable (() => ( - fail!("internal error: entered unreachable code"); - )) - - macro_rules! condition ( - - { pub $c:ident: $input:ty -> $out:ty; } => { - - pub mod $c { - #[allow(unused_imports)]; - #[allow(non_uppercase_statics)]; - #[allow(missing_doc)]; - - use super::*; - - local_data_key!(key: @::std::condition::Handler<$input, $out>) - - pub static cond : - ::std::condition::Condition<$input,$out> = - ::std::condition::Condition { - name: stringify!($c), - key: key - }; - } - }; - - { $c:ident: $input:ty -> $out:ty; } => { - - mod $c { - #[allow(unused_imports)]; - #[allow(non_uppercase_statics)]; - #[allow(dead_code)]; - - use super::*; - - local_data_key!(key: @::std::condition::Handler<$input, $out>) - - pub static cond : - ::std::condition::Condition<$input,$out> = - ::std::condition::Condition { - name: stringify!($c), - key: key - }; - } - } - ) - - macro_rules! format(($($arg:tt)*) => ( - format_args!(::std::fmt::format, $($arg)*) - )) - macro_rules! write(($dst:expr, $($arg:tt)*) => ( - format_args!(|args| { ::std::fmt::write($dst, args) }, $($arg)*) - )) - macro_rules! writeln(($dst:expr, $($arg:tt)*) => ( - format_args!(|args| { ::std::fmt::writeln($dst, args) }, $($arg)*) - )) - macro_rules! print ( - ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*)) - ) - macro_rules! println ( - ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*)) - ) - - macro_rules! local_data_key ( - ($name:ident: $ty:ty) => ( - static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key; - ); - (pub $name:ident: $ty:ty) => ( - pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key; - ) - ) -}"# -} - -struct Injector { - sm: @ast::Item, -} - -impl Folder for Injector { - fn fold_mod(&mut self, module: &ast::Mod) -> ast::Mod { - // Just inject the standard macros at the start of the first module - // in the crate: that is, at the start of the crate file itself. - let items = vec::append(~[ self.sm ], module.items); - ast::Mod { - items: items, - ..(*module).clone() // FIXME #2543: Bad copy. - } - } -} - -// add a bunch of macros as though they were placed at the head of the -// program (ick). This should run before cfg stripping. -pub fn inject_std_macros(parse_sess: @parse::ParseSess, - cfg: ast::CrateConfig, - c: Crate) - -> Crate { - let sm = match parse_item_from_source_str(@"", - std_macros(), - cfg.clone(), - parse_sess) { - Some(item) => item, - None => fail!("expected core macros to parse correctly") - }; - - let mut injector = Injector { - sm: sm, - }; - injector.fold_crate(c) -} - pub struct MacroExpander<'a> { extsbox: SyntaxEnv, cx: &'a mut ExtCtxt<'a>, @@ -1231,20 +1018,6 @@ mod test { } } - // make sure that fail! is present - #[test] fn fail_exists_test () { - let src = @"fn main() { fail!(\"something appropriately gloomy\");}"; - let sess = parse::new_parse_sess(None); - let crate_ast = parse::parse_crate_from_source_str( - @"", - src, - ~[],sess); - let crate_ast = inject_std_macros(sess, ~[], crate_ast); - // don't bother with striping, doesn't affect fail!. - let mut loader = ErrLoader; - expand_crate(sess,&mut loader,~[],crate_ast); - } - // these following tests are quite fragile, in that they don't test what // *kind* of failure occurs. @@ -1292,20 +1065,6 @@ mod test { expand_crate(sess,&mut loader,~[],crate_ast); } - #[test] fn std_macros_must_parse () { - let src = super::std_macros(); - let sess = parse::new_parse_sess(None); - let cfg = ~[]; - let item_ast = parse::parse_item_from_source_str( - @"", - src, - cfg,sess); - match item_ast { - Some(_) => (), // success - None => fail!("expected this to parse") - } - } - #[test] fn test_contains_flatten (){ let attr1 = make_dummy_attr (@"foo"); let attr2 = make_dummy_attr (@"bar"); diff --git a/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs b/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs index ebd3320d90126..0fc3b3912b17b 100644 --- a/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs +++ b/src/test/compile-fail/tag-that-dare-not-speak-its-name.rs @@ -11,9 +11,7 @@ // error-pattern:mismatched types: expected `char` but found // Issue #876 -#[no_std]; - -extern mod std; +#[no_implicit_prelude]; fn last(v: ~[&T]) -> std::option::Option { fail!();