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
This commit is contained in:
Steven Fackler 2014-01-23 23:40:54 -08:00
parent a5ab960d2e
commit 86a8b031f5
6 changed files with 247 additions and 271 deletions

View File

@ -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))

View File

@ -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<T>(x: T) -> codemap::Spanned<T> {
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)

View File

@ -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. */

192
src/libstd/macros.rs Normal file
View File

@ -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 <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.
#[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;
)
)

View File

@ -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>",
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(
@"<test>",
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(
@"<test>",
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");

View File

@ -11,9 +11,7 @@
// error-pattern:mismatched types: expected `char` but found
// Issue #876
#[no_std];
extern mod std;
#[no_implicit_prelude];
fn last<T>(v: ~[&T]) -> std::option::Option<T> {
fail!();