diff options
Diffstat (limited to 'src/libsyntax_ext')
24 files changed, 5230 insertions, 0 deletions
diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs new file mode 100644 index 00000000000..072be571221 --- /dev/null +++ b/src/libsyntax_ext/asm.rs @@ -0,0 +1,245 @@ +// 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 <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. + +/* + * Inline assembly support. + */ +use self::State::*; + +use syntax::ast; +use syntax::codemap; +use syntax::codemap::Span; +use syntax::ext::base; +use syntax::ext::base::*; +use syntax::feature_gate; +use syntax::parse::token::{intern, InternedString}; +use syntax::parse::token; +use syntax::ptr::P; +use syntax::ast::AsmDialect; + +enum State { + Asm, + Outputs, + Inputs, + Clobbers, + Options, + StateNone +} + +impl State { + fn next(&self) -> State { + match *self { + Asm => Outputs, + Outputs => Inputs, + Inputs => Clobbers, + Clobbers => Options, + Options => StateNone, + StateNone => StateNone + } + } +} + +const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"]; + +pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { + if !cx.ecfg.enable_asm() { + feature_gate::emit_feature_err( + &cx.parse_sess.span_diagnostic, "asm", sp, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_ASM); + return DummyResult::expr(sp); + } + + let mut p = cx.new_parser_from_tts(tts); + let mut asm = InternedString::new(""); + let mut asm_str_style = None; + let mut outputs = Vec::new(); + let mut inputs = Vec::new(); + let mut clobs = Vec::new(); + let mut volatile = false; + let mut alignstack = false; + let mut dialect = AsmDialect::Att; + + let mut state = Asm; + + 'statement: loop { + match state { + Asm => { + if asm_str_style.is_some() { + // If we already have a string with instructions, + // ending up in Asm state again is an error. + cx.span_err(sp, "malformed inline assembly"); + return DummyResult::expr(sp); + } + let (s, style) = match expr_to_string(cx, panictry!(p.parse_expr()), + "inline assembly must be a string literal") { + Some((s, st)) => (s, st), + // let compilation continue + None => return DummyResult::expr(sp), + }; + asm = s; + asm_str_style = Some(style); + } + Outputs => { + while p.token != token::Eof && + p.token != token::Colon && + p.token != token::ModSep { + + if !outputs.is_empty() { + panictry!(p.eat(&token::Comma)); + } + + let (constraint, _str_style) = panictry!(p.parse_str()); + + let span = p.last_span; + + panictry!(p.expect(&token::OpenDelim(token::Paren))); + let out = panictry!(p.parse_expr()); + panictry!(p.expect(&token::CloseDelim(token::Paren))); + + // Expands a read+write operand into two operands. + // + // Use '+' modifier when you want the same expression + // to be both an input and an output at the same time. + // It's the opposite of '=&' which means that the memory + // cannot be shared with any other operand (usually when + // a register is clobbered early.) + let output = match constraint.slice_shift_char() { + Some(('=', _)) => None, + Some(('+', operand)) => { + Some(token::intern_and_get_ident(&format!( + "={}", operand))) + } + _ => { + cx.span_err(span, "output operand constraint lacks '=' or '+'"); + None + } + }; + + let is_rw = output.is_some(); + let is_indirect = constraint.contains("*"); + outputs.push(ast::InlineAsmOutput { + constraint: output.unwrap_or(constraint), + expr: out, + is_rw: is_rw, + is_indirect: is_indirect, + }); + } + } + Inputs => { + while p.token != token::Eof && + p.token != token::Colon && + p.token != token::ModSep { + + if !inputs.is_empty() { + panictry!(p.eat(&token::Comma)); + } + + let (constraint, _str_style) = panictry!(p.parse_str()); + + if constraint.starts_with("=") { + cx.span_err(p.last_span, "input operand constraint contains '='"); + } else if constraint.starts_with("+") { + cx.span_err(p.last_span, "input operand constraint contains '+'"); + } + + panictry!(p.expect(&token::OpenDelim(token::Paren))); + let input = panictry!(p.parse_expr()); + panictry!(p.expect(&token::CloseDelim(token::Paren))); + + inputs.push((constraint, input)); + } + } + Clobbers => { + while p.token != token::Eof && + p.token != token::Colon && + p.token != token::ModSep { + + if !clobs.is_empty() { + panictry!(p.eat(&token::Comma)); + } + + let (s, _str_style) = panictry!(p.parse_str()); + + if OPTIONS.iter().any(|&opt| s == opt) { + cx.span_warn(p.last_span, "expected a clobber, found an option"); + } + clobs.push(s); + } + } + Options => { + let (option, _str_style) = panictry!(p.parse_str()); + + if option == "volatile" { + // Indicates that the inline assembly has side effects + // and must not be optimized out along with its outputs. + volatile = true; + } else if option == "alignstack" { + alignstack = true; + } else if option == "intel" { + dialect = AsmDialect::Intel; + } else { + cx.span_warn(p.last_span, "unrecognized option"); + } + + if p.token == token::Comma { + panictry!(p.eat(&token::Comma)); + } + } + StateNone => () + } + + loop { + // MOD_SEP is a double colon '::' without space in between. + // When encountered, the state must be advanced twice. + match (&p.token, state.next(), state.next().next()) { + (&token::Colon, StateNone, _) | + (&token::ModSep, _, StateNone) => { + panictry!(p.bump()); + break 'statement; + } + (&token::Colon, st, _) | + (&token::ModSep, _, st) => { + panictry!(p.bump()); + state = st; + } + (&token::Eof, _, _) => break 'statement, + _ => break + } + } + } + + let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: sp, + callee: codemap::NameAndSpan { + format: codemap::MacroBang(intern("asm")), + span: None, + allow_internal_unstable: false, + }, + }); + + MacEager::expr(P(ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprInlineAsm(ast::InlineAsm { + asm: token::intern_and_get_ident(&asm), + asm_str_style: asm_str_style.unwrap(), + outputs: outputs, + inputs: inputs, + clobbers: clobs, + volatile: volatile, + alignstack: alignstack, + dialect: dialect, + expn_id: expn_id, + }), + span: sp, + attrs: None, + })) +} diff --git a/src/libsyntax_ext/cfg.rs b/src/libsyntax_ext/cfg.rs new file mode 100644 index 00000000000..1e1bf538876 --- /dev/null +++ b/src/libsyntax_ext/cfg.rs @@ -0,0 +1,45 @@ +// 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 <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. + +/// The compiler code necessary to support the cfg! extension, which expands to +/// a literal `true` or `false` based on whether the given cfg matches the +/// current compilation environment. + +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::*; +use syntax::ext::base; +use syntax::ext::build::AstBuilder; +use syntax::attr; +use syntax::attr::*; +use syntax::parse::token; +use syntax::config::CfgDiagReal; + +pub fn expand_cfg<'cx>(cx: &mut ExtCtxt, + sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'static> { + let mut p = cx.new_parser_from_tts(tts); + let cfg = panictry!(p.parse_meta_item()); + + if !panictry!(p.eat(&token::Eof)){ + cx.span_err(sp, "expected 1 cfg-pattern"); + return DummyResult::expr(sp); + } + + let matches_cfg = { + let mut diag = CfgDiagReal { + diag: &cx.parse_sess.span_diagnostic, + feature_gated_cfgs: cx.feature_gated_cfgs, + }; + attr::cfg_matches(&cx.cfg, &cfg, &mut diag) + }; + MacEager::expr(cx.expr_bool(sp, matches_cfg)) +} diff --git a/src/libsyntax_ext/concat.rs b/src/libsyntax_ext/concat.rs new file mode 100644 index 00000000000..de913fe0431 --- /dev/null +++ b/src/libsyntax_ext/concat.rs @@ -0,0 +1,66 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast; +use syntax::codemap; +use syntax::ext::base; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; + +use std::string::String; + +pub fn expand_syntax_ext(cx: &mut base::ExtCtxt, + sp: codemap::Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'static> { + let es = match base::get_exprs_from_tts(cx, sp, tts) { + Some(e) => e, + None => return base::DummyResult::expr(sp) + }; + let mut accumulator = String::new(); + for e in es { + match e.node { + ast::ExprLit(ref lit) => { + match lit.node { + ast::LitStr(ref s, _) | + ast::LitFloat(ref s, _) | + ast::LitFloatUnsuffixed(ref s) => { + accumulator.push_str(&s); + } + ast::LitChar(c) => { + accumulator.push(c); + } + ast::LitInt(i, ast::UnsignedIntLit(_)) | + ast::LitInt(i, ast::SignedIntLit(_, ast::Plus)) | + ast::LitInt(i, ast::UnsuffixedIntLit(ast::Plus)) => { + accumulator.push_str(&format!("{}", i)); + } + ast::LitInt(i, ast::SignedIntLit(_, ast::Minus)) | + ast::LitInt(i, ast::UnsuffixedIntLit(ast::Minus)) => { + accumulator.push_str(&format!("-{}", i)); + } + ast::LitBool(b) => { + accumulator.push_str(&format!("{}", b)); + } + ast::LitByte(..) | + ast::LitByteStr(..) => { + cx.span_err(e.span, "cannot concatenate a byte string literal"); + } + } + } + _ => { + cx.span_err(e.span, "expected a literal"); + } + } + } + base::MacEager::expr(cx.expr_str( + sp, + token::intern_and_get_ident(&accumulator[..]))) +} diff --git a/src/libsyntax_ext/concat_idents.rs b/src/libsyntax_ext/concat_idents.rs new file mode 100644 index 00000000000..9702b24ffd4 --- /dev/null +++ b/src/libsyntax_ext/concat_idents.rs @@ -0,0 +1,73 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast::{self, TokenTree}; +use syntax::codemap::Span; +use syntax::ext::base::*; +use syntax::ext::base; +use syntax::feature_gate; +use syntax::parse::token; +use syntax::parse::token::str_to_ident; +use syntax::ptr::P; + +pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) + -> Box<base::MacResult+'cx> { + if !cx.ecfg.enable_concat_idents() { + feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, + "concat_idents", + sp, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CONCAT_IDENTS); + return base::DummyResult::expr(sp); + } + + let mut res_str = String::new(); + for (i, e) in tts.iter().enumerate() { + if i & 1 == 1 { + match *e { + TokenTree::Token(_, token::Comma) => {}, + _ => { + cx.span_err(sp, "concat_idents! expecting comma."); + return DummyResult::expr(sp); + }, + } + } else { + match *e { + TokenTree::Token(_, token::Ident(ident, _)) => { + res_str.push_str(&ident.name.as_str()) + }, + _ => { + cx.span_err(sp, "concat_idents! requires ident args."); + return DummyResult::expr(sp); + }, + } + } + } + let res = str_to_ident(&res_str); + + let e = P(ast::Expr { + id: ast::DUMMY_NODE_ID, + node: ast::ExprPath(None, + ast::Path { + span: sp, + global: false, + segments: vec!( + ast::PathSegment { + identifier: res, + parameters: ast::PathParameters::none(), + } + ) + } + ), + span: sp, + attrs: None, + }); + MacEager::expr(e) +} diff --git a/src/libsyntax_ext/deriving/bounds.rs b/src/libsyntax_ext/deriving/bounds.rs new file mode 100644 index 00000000000..9bc0e088110 --- /dev/null +++ b/src/libsyntax_ext/deriving/bounds.rs @@ -0,0 +1,50 @@ +// 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. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::MetaItem; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; + +pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt, + span: Span, + _: &MetaItem, + _: &Annotatable, + _: &mut FnMut(Annotatable)) +{ + cx.span_err(span, "this unsafe trait should be implemented explicitly"); +} + +pub fn expand_deriving_copy(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + let mut v = cx.crate_root.map(|s| vec![s]).unwrap_or(Vec::new()); + v.push("marker"); + v.push("Copy"); + let path = Path::new(v); + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path, + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: Vec::new(), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push); +} diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs new file mode 100644 index 00000000000..1825c3d347e --- /dev/null +++ b/src/libsyntax_ext/deriving/clone.rs @@ -0,0 +1,115 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_clone(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::clone::Clone), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "clone", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: Vec::new(), + ret_ty: Self_, + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_clone("Clone", c, s, sub) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn cs_clone( + name: &str, + cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure) -> P<Expr> { + let ctor_path; + let all_fields; + let fn_path = cx.std_path(&["clone", "Clone", "clone"]); + let subcall = |field: &FieldInfo| { + let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; + + cx.expr_call_global(field.span, fn_path.clone(), args) + }; + + match *substr.fields { + Struct(ref af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident]); + all_fields = af; + } + EnumMatching(_, variant, ref af) => { + ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]); + all_fields = af; + }, + EnumNonMatchingCollapsed (..) => { + cx.span_bug(trait_span, + &format!("non-matching enum variants in \ + `derive({})`", name)) + } + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, + &format!("static method in `derive({})`", name)) + } + } + + if !all_fields.is_empty() && all_fields[0].name.is_none() { + // enum-like + let subcalls = all_fields.iter().map(subcall).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) + } else { + // struct-like + let fields = all_fields.iter().map(|field| { + let ident = match field.name { + Some(i) => i, + None => { + cx.span_bug(trait_span, + &format!("unnamed field in normal struct in \ + `derive({})`", name)) + } + }; + cx.field_imm(field.span, ident, subcall(field)) + }).collect::<Vec<_>>(); + + if fields.is_empty() { + // no fields, so construct like `None` + cx.expr_path(ctor_path) + } else { + cx.expr_struct(trait_span, ctor_path, fields) + } + } +} diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs new file mode 100644 index 00000000000..1b855c56a48 --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -0,0 +1,73 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_eq(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { + cs_same_method( + |cx, span, exprs| { + // create `a.<method>(); b.<method>(); c.<method>(); ...` + // (where method is `assert_receiver_is_total_eq`) + let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect(); + let block = cx.block(span, stmts, None); + cx.expr_block(block) + }, + Box::new(|cx, sp, _, _| { + cx.span_bug(sp, "non matching enums in derive(Eq)?") }), + cx, + span, + substr + ) + } + + let inline = cx.meta_word(span, InternedString::new("inline")); + let hidden = cx.meta_word(span, InternedString::new("hidden")); + let doc = cx.meta_list(span, InternedString::new("doc"), vec!(hidden)); + let attrs = vec!(cx.attribute(span, inline), + cx.attribute(span, doc)); + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::cmp::Eq), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "assert_receiver_is_total_eq", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(), + ret_ty: nil_ty(), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + cs_total_eq_assert(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} diff --git a/src/libsyntax_ext/deriving/cmp/ord.rs b/src/libsyntax_ext/deriving/cmp/ord.rs new file mode 100644 index 00000000000..95a5d184d0e --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/ord.rs @@ -0,0 +1,136 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_ord(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::cmp::Ord), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "cmp", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(borrowed_self()), + ret_ty: Literal(path_std!(cx, core::cmp::Ordering)), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + cs_cmp(a, b, c) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + + +pub fn ordering_collapsed(cx: &mut ExtCtxt, + span: Span, + self_arg_tags: &[ast::Ident]) -> P<ast::Expr> { + let lft = cx.expr_ident(span, self_arg_tags[0]); + let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); + cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt]) +} + +pub fn cs_cmp(cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P<Expr> { + let test_id = cx.ident_of("__test"); + let equals_path = cx.path_global(span, + cx.std_path(&["cmp", "Ordering", "Equal"])); + + let cmp_path = cx.std_path(&["cmp", "Ord", "cmp"]); + + /* + Builds: + + let __test = ::std::cmp::Ord::cmp(&self_field1, &other_field1); + if other == ::std::cmp::Ordering::Equal { + let __test = ::std::cmp::Ord::cmp(&self_field2, &other_field2); + if __test == ::std::cmp::Ordering::Equal { + ... + } else { + __test + } + } else { + __test + } + + FIXME #6449: These `if`s could/should be `match`es. + */ + cs_fold( + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. + false, + |cx, span, old, self_f, other_fs| { + // let __test = new; + // if __test == ::std::cmp::Ordering::Equal { + // old + // } else { + // __test + // } + + let new = { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"), + }; + + let args = vec![ + cx.expr_addr_of(span, self_f), + cx.expr_addr_of(span, other_f.clone()), + ]; + + cx.expr_call_global(span, cmp_path.clone(), args) + }; + + let assign = cx.stmt_let(span, false, test_id, new); + + let cond = cx.expr_binary(span, ast::BiEq, + cx.expr_ident(span, test_id), + cx.expr_path(equals_path.clone())); + let if_ = cx.expr_if(span, + cond, + old, Some(cx.expr_ident(span, test_id))); + cx.expr_block(cx.block(span, vec!(assign), Some(if_))) + }, + cx.expr_path(equals_path.clone()), + Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derives(Ord)`") + } else { + ordering_collapsed(cx, span, tag_tuple) + } + }), + cx, span, substr) +} diff --git a/src/libsyntax_ext/deriving/cmp/partial_eq.rs b/src/libsyntax_ext/deriving/cmp/partial_eq.rs new file mode 100644 index 00000000000..29be5a7ddc3 --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/partial_eq.rs @@ -0,0 +1,97 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr, self}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + // structures are equal if all fields are equal, and non equal, if + // any fields are not equal or if the enum variants are different + fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { + cs_fold( + true, // use foldl + |cx, span, subexpr, self_f, other_fs| { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`") + }; + + let eq = cx.expr_binary(span, ast::BiEq, self_f, other_f.clone()); + + cx.expr_binary(span, ast::BiAnd, subexpr, eq) + }, + cx.expr_bool(span, true), + Box::new(|cx, span, _, _| cx.expr_bool(span, false)), + cx, span, substr) + } + fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> { + cs_fold( + true, // use foldl + |cx, span, subexpr, self_f, other_fs| { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`") + }; + + let eq = cx.expr_binary(span, ast::BiNe, self_f, other_f.clone()); + + cx.expr_binary(span, ast::BiOr, subexpr, eq) + }, + cx.expr_bool(span, false), + Box::new(|cx, span, _, _| cx.expr_bool(span, true)), + cx, span, substr) + } + + macro_rules! md { + ($name:expr, $f:ident) => { { + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + MethodDef { + name: $name, + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(borrowed_self()), + ret_ty: Literal(path_local!(bool)), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + $f(a, b, c) + })) + } + } } + } + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::cmp::PartialEq), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + md!("eq", cs_eq), + md!("ne", cs_ne) + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} diff --git a/src/libsyntax_ext/deriving/cmp/partial_ord.rs b/src/libsyntax_ext/deriving/cmp/partial_ord.rs new file mode 100644 index 00000000000..bd825e5c8df --- /dev/null +++ b/src/libsyntax_ext/deriving/cmp/partial_ord.rs @@ -0,0 +1,233 @@ +// 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 <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. + +pub use self::OrderingOp::*; + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + macro_rules! md { + ($name:expr, $op:expr, $equal:expr) => { { + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + MethodDef { + name: $name, + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(borrowed_self()), + ret_ty: Literal(path_local!(bool)), + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|cx, span, substr| { + cs_op($op, $equal, cx, span, substr) + })) + } + } } + } + + let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering)); + let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option), + None, + vec![Box::new(ordering_ty)], + true)); + + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + + let partial_cmp_def = MethodDef { + name: "partial_cmp", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec![borrowed_self()], + ret_ty: ret_ty, + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|cx, span, substr| { + cs_partial_cmp(cx, span, substr) + })) + }; + + let trait_def = TraitDef { + span: span, + attributes: vec![], + path: path_std!(cx, core::cmp::PartialOrd), + additional_bounds: vec![], + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec![ + partial_cmp_def, + md!("lt", true, false), + md!("le", true, true), + md!("gt", false, false), + md!("ge", false, true) + ], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +#[derive(Copy, Clone)] +pub enum OrderingOp { + PartialCmpOp, LtOp, LeOp, GtOp, GeOp, +} + +pub fn some_ordering_collapsed(cx: &mut ExtCtxt, + span: Span, + op: OrderingOp, + self_arg_tags: &[ast::Ident]) -> P<ast::Expr> { + let lft = cx.expr_ident(span, self_arg_tags[0]); + let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); + let op_str = match op { + PartialCmpOp => "partial_cmp", + LtOp => "lt", LeOp => "le", + GtOp => "gt", GeOp => "ge", + }; + cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt]) +} + +pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P<Expr> { + let test_id = cx.ident_of("__test"); + let ordering = cx.path_global(span, + cx.std_path(&["cmp", "Ordering", "Equal"])); + let ordering = cx.expr_path(ordering); + let equals_expr = cx.expr_some(span, ordering); + + let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]); + + /* + Builds: + + let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1); + if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) { + let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2); + if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) { + ... + } else { + __test + } + } else { + __test + } + + FIXME #6449: These `if`s could/should be `match`es. + */ + cs_fold( + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. + false, + |cx, span, old, self_f, other_fs| { + // let __test = new; + // if __test == Some(::std::cmp::Ordering::Equal) { + // old + // } else { + // __test + // } + + let new = { + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"), + }; + + let args = vec![ + cx.expr_addr_of(span, self_f), + cx.expr_addr_of(span, other_f.clone()), + ]; + + cx.expr_call_global(span, partial_cmp_path.clone(), args) + }; + + let assign = cx.stmt_let(span, false, test_id, new); + + let cond = cx.expr_binary(span, ast::BiEq, + cx.expr_ident(span, test_id), + equals_expr.clone()); + let if_ = cx.expr_if(span, + cond, + old, Some(cx.expr_ident(span, test_id))); + cx.expr_block(cx.block(span, vec!(assign), Some(if_))) + }, + equals_expr.clone(), + Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + } else { + some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple) + } + }), + cx, span, substr) +} + +/// Strict inequality. +fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, + span: Span, substr: &Substructure) -> P<Expr> { + let op = if less {ast::BiLt} else {ast::BiGt}; + cs_fold( + false, // need foldr, + |cx, span, subexpr, self_f, other_fs| { + /* + build up a series of chain ||'s and &&'s from the inside + out (hence foldr) to get lexical ordering, i.e. for op == + `ast::lt` + + ``` + self.f1 < other.f1 || (!(other.f1 < self.f1) && + (self.f2 < other.f2 || (!(other.f2 < self.f2) && + (false) + )) + ) + ``` + + The optimiser should remove the redundancy. We explicitly + get use the binops to avoid auto-deref dereferencing too many + layers of pointers, if the type includes pointers. + */ + let other_f = match (other_fs.len(), other_fs.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + }; + + let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone()); + + let not_cmp = cx.expr_unary(span, ast::UnNot, + cx.expr_binary(span, op, other_f.clone(), self_f)); + + let and = cx.expr_binary(span, ast::BiAnd, not_cmp, subexpr); + cx.expr_binary(span, ast::BiOr, cmp, and) + }, + cx.expr_bool(span, equal), + Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| { + if self_args.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + } else { + let op = match (less, equal) { + (true, true) => LeOp, (true, false) => LtOp, + (false, true) => GeOp, (false, false) => GtOp, + }; + some_ordering_collapsed(cx, span, op, tag_tuple) + } + }), + cx, span, substr) +} diff --git a/src/libsyntax_ext/deriving/debug.rs b/src/libsyntax_ext/deriving/debug.rs new file mode 100644 index 00000000000..ed3f764c1d2 --- /dev/null +++ b/src/libsyntax_ext/deriving/debug.rs @@ -0,0 +1,156 @@ +// 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. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::{Span, respan}; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; +use syntax::ptr::P; + +pub fn expand_deriving_debug(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + // &mut ::std::fmt::Formatter + let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))), + Borrowed(None, ast::MutMutable)); + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::fmt::Debug), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec![ + MethodDef { + name: "fmt", + generics: LifetimeBounds::empty(), + explicit_self: borrowed_explicit_self(), + args: vec!(fmtr), + ret_ty: Literal(path_std!(cx, core::fmt::Result)), + attributes: Vec::new(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + show_substructure(a, b, c) + })) + } + ], + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +/// We use the debug builders to do the heavy lifting here +fn show_substructure(cx: &mut ExtCtxt, span: Span, + substr: &Substructure) -> P<Expr> { + // build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build() + // or fmt.debug_tuple(<name>).field(&<fieldval>)....build() + // based on the "shape". + let ident = match *substr.fields { + Struct(_) => substr.type_ident, + EnumMatching(_, v, _) => v.node.name, + EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => { + cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`") + } + }; + + // We want to make sure we have the expn_id set so that we can use unstable methods + let span = Span { expn_id: cx.backtrace(), .. span }; + let name = cx.expr_lit(span, ast::Lit_::LitStr(ident.name.as_str(), + ast::StrStyle::CookedStr)); + let builder = token::str_to_ident("builder"); + let builder_expr = cx.expr_ident(span, builder.clone()); + + let fmt = substr.nonself_args[0].clone(); + + let stmts = match *substr.fields { + Struct(ref fields) | EnumMatching(_, _, ref fields) => { + let mut stmts = vec![]; + if fields.is_empty() || fields[0].name.is_none() { + // tuple struct/"normal" variant + let expr = cx.expr_method_call(span, + fmt, + token::str_to_ident("debug_tuple"), + vec![name]); + stmts.push(cx.stmt_let(span, true, builder, expr)); + + for field in fields { + // Use double indirection to make sure this works for unsized types + let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field); + + let expr = cx.expr_method_call(span, + builder_expr.clone(), + token::str_to_ident("field"), + vec![field]); + + // Use `let _ = expr;` to avoid triggering the + // unused_results lint. + stmts.push(stmt_let_undescore(cx, span, expr)); + } + } else { + // normal struct/struct variant + let expr = cx.expr_method_call(span, + fmt, + token::str_to_ident("debug_struct"), + vec![name]); + stmts.push(cx.stmt_let(span, true, builder, expr)); + + for field in fields { + let name = cx.expr_lit(field.span, ast::Lit_::LitStr( + field.name.unwrap().name.as_str(), + ast::StrStyle::CookedStr)); + + // Use double indirection to make sure this works for unsized types + let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field); + let expr = cx.expr_method_call(span, + builder_expr.clone(), + token::str_to_ident("field"), + vec![name, field]); + stmts.push(stmt_let_undescore(cx, span, expr)); + } + } + stmts + } + _ => unreachable!() + }; + + let expr = cx.expr_method_call(span, + builder_expr, + token::str_to_ident("finish"), + vec![]); + + let block = cx.block(span, stmts, Some(expr)); + cx.expr_block(block) +} + +fn stmt_let_undescore(cx: &mut ExtCtxt, + sp: Span, + expr: P<ast::Expr>) -> P<ast::Stmt> { + let local = P(ast::Local { + pat: cx.pat_wild(sp), + ty: None, + init: Some(expr), + id: ast::DUMMY_NODE_ID, + span: sp, + attrs: None, + }); + let decl = respan(sp, ast::DeclLocal(local)); + P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) +} diff --git a/src/libsyntax_ext/deriving/decodable.rs b/src/libsyntax_ext/deriving/decodable.rs new file mode 100644 index 00000000000..4ea4f04623a --- /dev/null +++ b/src/libsyntax_ext/deriving/decodable.rs @@ -0,0 +1,224 @@ +// 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 <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. + +//! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast; +use syntax::ast::{MetaItem, Expr, MutMutable}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::parse::token; +use syntax::ptr::P; + +pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize") +} + +pub fn expand_deriving_decodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize") +} + +fn expand_deriving_decodable_imp(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable), + krate: &'static str) +{ + if cx.crate_root != Some("std") { + // FIXME(#21880): lift this requirement. + cx.span_err(span, "this trait cannot be derived with #![no_std] \ + or #![no_core]"); + return + } + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "decode", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec!(("__D", vec!(Path::new_( + vec!(krate, "Decoder"), None, + vec!(), true)))) + }, + explicit_self: None, + args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))), + Borrowed(None, MutMutable))), + ret_ty: Literal(Path::new_( + pathvec_std!(cx, core::result::Result), + None, + vec!(Box::new(Self_), Box::new(Literal(Path::new_( + vec!["__D", "Error"], None, vec![], false + )))), + true + )), + attributes: Vec::new(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + decodable_substructure(a, b, c, krate) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure, + krate: &str) -> P<Expr> { + let decoder = substr.nonself_args[0].clone(); + let recurse = vec!(cx.ident_of(krate), + cx.ident_of("Decodable"), + cx.ident_of("decode")); + let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse)); + // throw an underscore in front to suppress unused variable warnings + let blkarg = cx.ident_of("_d"); + let blkdecoder = cx.expr_ident(trait_span, blkarg); + + return match *substr.fields { + StaticStruct(_, ref summary) => { + let nfields = match *summary { + Unnamed(ref fields) => fields.len(), + Named(ref fields) => fields.len() + }; + let read_struct_field = cx.ident_of("read_struct_field"); + + let path = cx.path_ident(trait_span, substr.type_ident); + let result = decode_static_fields(cx, + trait_span, + path, + summary, + |cx, span, name, field| { + cx.expr_try(span, + cx.expr_method_call(span, blkdecoder.clone(), read_struct_field, + vec!(cx.expr_str(span, name), + cx.expr_usize(span, field), + exprdecode.clone()))) + }); + let result = cx.expr_ok(trait_span, result); + cx.expr_method_call(trait_span, + decoder, + cx.ident_of("read_struct"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + cx.expr_usize(trait_span, nfields), + cx.lambda_expr_1(trait_span, result, blkarg) + )) + } + StaticEnum(_, ref fields) => { + let variant = cx.ident_of("i"); + + let mut arms = Vec::new(); + let mut variants = Vec::new(); + let rvariant_arg = cx.ident_of("read_enum_variant_arg"); + + for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() { + variants.push(cx.expr_str(v_span, ident.name.as_str())); + + let path = cx.path(trait_span, vec![substr.type_ident, ident]); + let decoded = decode_static_fields(cx, + v_span, + path, + parts, + |cx, span, _, field| { + let idx = cx.expr_usize(span, field); + cx.expr_try(span, + cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg, + vec!(idx, exprdecode.clone()))) + }); + + arms.push(cx.arm(v_span, + vec!(cx.pat_lit(v_span, cx.expr_usize(v_span, i))), + decoded)); + } + + arms.push(cx.arm_unreachable(trait_span)); + + let result = cx.expr_ok(trait_span, + cx.expr_match(trait_span, + cx.expr_ident(trait_span, variant), arms)); + let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result); + let variant_vec = cx.expr_vec(trait_span, variants); + let variant_vec = cx.expr_addr_of(trait_span, variant_vec); + let result = cx.expr_method_call(trait_span, blkdecoder, + cx.ident_of("read_enum_variant"), + vec!(variant_vec, lambda)); + cx.expr_method_call(trait_span, + decoder, + cx.ident_of("read_enum"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + cx.lambda_expr_1(trait_span, result, blkarg) + )) + } + _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)") + }; +} + +/// Create a decoder for a single enum variant/struct: +/// - `outer_pat_path` is the path to this enum variant/struct +/// - `getarg` should retrieve the `usize`-th field with name `@str`. +fn decode_static_fields<F>(cx: &mut ExtCtxt, + trait_span: Span, + outer_pat_path: ast::Path, + fields: &StaticFields, + mut getarg: F) + -> P<Expr> where + F: FnMut(&mut ExtCtxt, Span, InternedString, usize) -> P<Expr>, +{ + match *fields { + Unnamed(ref fields) => { + let path_expr = cx.expr_path(outer_pat_path); + if fields.is_empty() { + path_expr + } else { + let fields = fields.iter().enumerate().map(|(i, &span)| { + getarg(cx, span, + token::intern_and_get_ident(&format!("_field{}", i)), + i) + }).collect(); + + cx.expr_call(trait_span, path_expr, fields) + } + } + Named(ref fields) => { + // use the field's span to get nicer error messages. + let fields = fields.iter().enumerate().map(|(i, &(ident, span))| { + let arg = getarg(cx, span, ident.name.as_str(), i); + cx.field_imm(span, ident, arg) + }).collect(); + cx.expr_struct(trait_span, outer_pat_path, fields) + } + } +} diff --git a/src/libsyntax_ext/deriving/default.rs b/src/libsyntax_ext/deriving/default.rs new file mode 100644 index 00000000000..bee63a98c25 --- /dev/null +++ b/src/libsyntax_ext/deriving/default.rs @@ -0,0 +1,85 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_default(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::default::Default), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "default", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: Vec::new(), + ret_ty: Self_, + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + default_substructure(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + trait_def.expand(cx, mitem, item, push) +} + +fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { + let default_ident = cx.std_path(&["default", "Default", "default"]); + let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); + + return match *substr.fields { + StaticStruct(_, ref summary) => { + match *summary { + Unnamed(ref fields) => { + if fields.is_empty() { + cx.expr_ident(trait_span, substr.type_ident) + } else { + let exprs = fields.iter().map(|sp| default_call(*sp)).collect(); + cx.expr_call_ident(trait_span, substr.type_ident, exprs) + } + } + Named(ref fields) => { + let default_fields = fields.iter().map(|&(ident, span)| { + cx.field_imm(span, ident, default_call(span)) + }).collect(); + cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) + } + } + } + StaticEnum(..) => { + cx.span_err(trait_span, "`Default` cannot be derived for enums, only structs"); + // let compilation continue + cx.expr_usize(trait_span, 0) + } + _ => cx.span_bug(trait_span, "Non-static method in `derive(Default)`") + }; +} diff --git a/src/libsyntax_ext/deriving/encodable.rs b/src/libsyntax_ext/deriving/encodable.rs new file mode 100644 index 00000000000..02747d38c00 --- /dev/null +++ b/src/libsyntax_ext/deriving/encodable.rs @@ -0,0 +1,289 @@ +// 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 <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. + +//! The compiler code necessary to implement the `#[derive(Encodable)]` +//! (and `Decodable`, in decodable.rs) extension. The idea here is that +//! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`. +//! +//! For example, a type like: +//! +//! ```ignore +//! #[derive(Encodable, Decodable)] +//! struct Node { id: usize } +//! ``` +//! +//! would generate two implementations like: +//! +//! ```ignore +//! impl<S: Encoder<E>, E> Encodable<S, E> for Node { +//! fn encode(&self, s: &mut S) -> Result<(), E> { +//! s.emit_struct("Node", 1, |this| { +//! this.emit_struct_field("id", 0, |this| { +//! Encodable::encode(&self.id, this) +//! /* this.emit_usize(self.id) can also be used */ +//! }) +//! }) +//! } +//! } +//! +//! impl<D: Decoder<E>, E> Decodable<D, E> for Node { +//! fn decode(d: &mut D) -> Result<Node, E> { +//! d.read_struct("Node", 1, |this| { +//! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) { +//! Ok(id) => Ok(Node { id: id }), +//! Err(e) => Err(e), +//! } +//! }) +//! } +//! } +//! ``` +//! +//! Other interesting scenarios are when the item has type parameters or +//! references other non-built-in types. A type definition like: +//! +//! ```ignore +//! #[derive(Encodable, Decodable)] +//! struct Spanned<T> { node: T, span: Span } +//! ``` +//! +//! would yield functions like: +//! +//! ```ignore +//! impl< +//! S: Encoder<E>, +//! E, +//! T: Encodable<S, E> +//! > Encodable<S, E> for Spanned<T> { +//! fn encode(&self, s: &mut S) -> Result<(), E> { +//! s.emit_struct("Spanned", 2, |this| { +//! this.emit_struct_field("node", 0, |this| self.node.encode(this)) +//! .unwrap(); +//! this.emit_struct_field("span", 1, |this| self.span.encode(this)) +//! }) +//! } +//! } +//! +//! impl< +//! D: Decoder<E>, +//! E, +//! T: Decodable<D, E> +//! > Decodable<D, E> for Spanned<T> { +//! fn decode(d: &mut D) -> Result<Spanned<T>, E> { +//! d.read_struct("Spanned", 2, |this| { +//! Ok(Spanned { +//! node: this.read_struct_field("node", 0, |this| Decodable::decode(this)) +//! .unwrap(), +//! span: this.read_struct_field("span", 1, |this| Decodable::decode(this)) +//! .unwrap(), +//! }) +//! }) +//! } +//! } +//! ``` + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr, ExprRet, MutMutable}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt,Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; +use syntax::ptr::P; + +pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize") +} + +pub fn expand_deriving_encodable(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize") +} + +fn expand_deriving_encodable_imp(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable), + krate: &'static str) +{ + if cx.crate_root != Some("std") { + // FIXME(#21880): lift this requirement. + cx.span_err(span, "this trait cannot be derived with #![no_std] \ + or #![no_core]"); + return; + } + + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "encode", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec!(("__S", vec!(Path::new_( + vec!(krate, "Encoder"), None, + vec!(), true)))) + }, + explicit_self: borrowed_explicit_self(), + args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))), + Borrowed(None, MutMutable))), + ret_ty: Literal(Path::new_( + pathvec_std!(cx, core::result::Result), + None, + vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_( + vec!["__S", "Error"], None, vec![], false + )))), + true + )), + attributes: Vec::new(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + encodable_substructure(a, b, c) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span, + substr: &Substructure) -> P<Expr> { + let encoder = substr.nonself_args[0].clone(); + // throw an underscore in front to suppress unused variable warnings + let blkarg = cx.ident_of("_e"); + let blkencoder = cx.expr_ident(trait_span, blkarg); + let encode = cx.ident_of("encode"); + + return match *substr.fields { + Struct(ref fields) => { + let emit_struct_field = cx.ident_of("emit_struct_field"); + let mut stmts = Vec::new(); + for (i, &FieldInfo { + name, + ref self_, + span, + .. + }) in fields.iter().enumerate() { + let name = match name { + Some(id) => id.name.as_str(), + None => { + token::intern_and_get_ident(&format!("_field{}", i)) + } + }; + let enc = cx.expr_method_call(span, self_.clone(), + encode, vec!(blkencoder.clone())); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder.clone(), + emit_struct_field, + vec!(cx.expr_str(span, name), + cx.expr_usize(span, i), + lambda)); + + // last call doesn't need a try! + let last = fields.len() - 1; + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprRet(Some(call))) + }; + stmts.push(cx.stmt_expr(call)); + } + + // unit structs have no fields and need to return Ok() + if stmts.is_empty() { + let ret_ok = cx.expr(trait_span, + ExprRet(Some(cx.expr_ok(trait_span, + cx.expr_tuple(trait_span, vec![]))))); + stmts.push(cx.stmt_expr(ret_ok)); + } + + let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); + cx.expr_method_call(trait_span, + encoder, + cx.ident_of("emit_struct"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + cx.expr_usize(trait_span, fields.len()), + blk + )) + } + + EnumMatching(idx, variant, ref fields) => { + // We're not generating an AST that the borrow checker is expecting, + // so we need to generate a unique local variable to take the + // mutable loan out on, otherwise we get conflicts which don't + // actually exist. + let me = cx.stmt_let(trait_span, false, blkarg, encoder); + let encoder = cx.expr_ident(trait_span, blkarg); + let emit_variant_arg = cx.ident_of("emit_enum_variant_arg"); + let mut stmts = Vec::new(); + if !fields.is_empty() { + let last = fields.len() - 1; + for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { + let enc = cx.expr_method_call(span, self_.clone(), + encode, vec!(blkencoder.clone())); + let lambda = cx.lambda_expr_1(span, enc, blkarg); + let call = cx.expr_method_call(span, blkencoder.clone(), + emit_variant_arg, + vec!(cx.expr_usize(span, i), + lambda)); + let call = if i != last { + cx.expr_try(span, call) + } else { + cx.expr(span, ExprRet(Some(call))) + }; + stmts.push(cx.stmt_expr(call)); + } + } else { + let ret_ok = cx.expr(trait_span, + ExprRet(Some(cx.expr_ok(trait_span, + cx.expr_tuple(trait_span, vec![]))))); + stmts.push(cx.stmt_expr(ret_ok)); + } + + let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); + let name = cx.expr_str(trait_span, variant.node.name.name.as_str()); + let call = cx.expr_method_call(trait_span, blkencoder, + cx.ident_of("emit_enum_variant"), + vec!(name, + cx.expr_usize(trait_span, idx), + cx.expr_usize(trait_span, fields.len()), + blk)); + let blk = cx.lambda_expr_1(trait_span, call, blkarg); + let ret = cx.expr_method_call(trait_span, + encoder, + cx.ident_of("emit_enum"), + vec!( + cx.expr_str(trait_span, substr.type_ident.name.as_str()), + blk + )); + cx.expr_block(cx.block(trait_span, vec!(me), Some(ret))) + } + + _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)") + }; +} diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs new file mode 100644 index 00000000000..5977144dae7 --- /dev/null +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -0,0 +1,1634 @@ +// 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 <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. + +//! Some code that abstracts away much of the boilerplate of writing +//! `derive` instances for traits. Among other things it manages getting +//! access to the fields of the 4 different sorts of structs and enum +//! variants, as well as creating the method and impl ast instances. +//! +//! Supported features (fairly exhaustive): +//! +//! - Methods taking any number of parameters of any type, and returning +//! any type, other than vectors, bottom and closures. +//! - Generating `impl`s for types with type parameters and lifetimes +//! (e.g. `Option<T>`), the parameters are automatically given the +//! current trait as a bound. (This includes separate type parameters +//! and lifetimes for methods.) +//! - Additional bounds on the type parameters (`TraitDef.additional_bounds`) +//! +//! The most important thing for implementers is the `Substructure` and +//! `SubstructureFields` objects. The latter groups 5 possibilities of the +//! arguments: +//! +//! - `Struct`, when `Self` is a struct (including tuple structs, e.g +//! `struct T(i32, char)`). +//! - `EnumMatching`, when `Self` is an enum and all the arguments are the +//! same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`) +//! - `EnumNonMatchingCollapsed` when `Self` is an enum and the arguments +//! are not the same variant (e.g. `None`, `Some(1)` and `None`). +//! - `StaticEnum` and `StaticStruct` for static methods, where the type +//! being derived upon is either an enum or struct respectively. (Any +//! argument with type Self is just grouped among the non-self +//! arguments.) +//! +//! In the first two cases, the values from the corresponding fields in +//! all the arguments are grouped together. For `EnumNonMatchingCollapsed` +//! this isn't possible (different variants have different fields), so the +//! fields are inaccessible. (Previous versions of the deriving infrastructure +//! had a way to expand into code that could access them, at the cost of +//! generating exponential amounts of code; see issue #15375). There are no +//! fields with values in the static cases, so these are treated entirely +//! differently. +//! +//! The non-static cases have `Option<ident>` in several places associated +//! with field `expr`s. This represents the name of the field it is +//! associated with. It is only not `None` when the associated field has +//! an identifier in the source code. For example, the `x`s in the +//! following snippet +//! +//! ```rust +//! # #![allow(dead_code)] +//! struct A { x : i32 } +//! +//! struct B(i32); +//! +//! enum C { +//! C0(i32), +//! C1 { x: i32 } +//! } +//! ``` +//! +//! The `i32`s in `B` and `C0` don't have an identifier, so the +//! `Option<ident>`s would be `None` for them. +//! +//! In the static cases, the structure is summarised, either into the just +//! spans of the fields or a list of spans and the field idents (for tuple +//! structs and record structs, respectively), or a list of these, for +//! enums (one for each variant). For empty struct and empty enum +//! variants, it is represented as a count of 0. +//! +//! # "`cs`" functions +//! +//! The `cs_...` functions ("combine substructure) are designed to +//! make life easier by providing some pre-made recipes for common +//! threads; mostly calling the function being derived on all the +//! arguments and then combining them back together in some way (or +//! letting the user chose that). They are not meant to be the only +//! way to handle the structures that this code creates. +//! +//! # Examples +//! +//! The following simplified `PartialEq` is used for in-code examples: +//! +//! ```rust +//! trait PartialEq { +//! fn eq(&self, other: &Self) -> bool; +//! } +//! impl PartialEq for i32 { +//! fn eq(&self, other: &i32) -> bool { +//! *self == *other +//! } +//! } +//! ``` +//! +//! Some examples of the values of `SubstructureFields` follow, using the +//! above `PartialEq`, `A`, `B` and `C`. +//! +//! ## Structs +//! +//! When generating the `expr` for the `A` impl, the `SubstructureFields` is +//! +//! ```{.text} +//! Struct(vec![FieldInfo { +//! span: <span of x> +//! name: Some(<ident of x>), +//! self_: <expr for &self.x>, +//! other: vec![<expr for &other.x] +//! }]) +//! ``` +//! +//! For the `B` impl, called with `B(a)` and `B(b)`, +//! +//! ```{.text} +//! Struct(vec![FieldInfo { +//! span: <span of `i32`>, +//! name: None, +//! self_: <expr for &a> +//! other: vec![<expr for &b>] +//! }]) +//! ``` +//! +//! ## Enums +//! +//! When generating the `expr` for a call with `self == C0(a)` and `other +//! == C0(b)`, the SubstructureFields is +//! +//! ```{.text} +//! EnumMatching(0, <ast::Variant for C0>, +//! vec![FieldInfo { +//! span: <span of i32> +//! name: None, +//! self_: <expr for &a>, +//! other: vec![<expr for &b>] +//! }]) +//! ``` +//! +//! For `C1 {x}` and `C1 {x}`, +//! +//! ```{.text} +//! EnumMatching(1, <ast::Variant for C1>, +//! vec![FieldInfo { +//! span: <span of x> +//! name: Some(<ident of x>), +//! self_: <expr for &self.x>, +//! other: vec![<expr for &other.x>] +//! }]) +//! ``` +//! +//! For `C0(a)` and `C1 {x}` , +//! +//! ```{.text} +//! EnumNonMatchingCollapsed( +//! vec![<ident of self>, <ident of __arg_1>], +//! &[<ast::Variant for C0>, <ast::Variant for C1>], +//! &[<ident for self index value>, <ident of __arg_1 index value>]) +//! ``` +//! +//! It is the same for when the arguments are flipped to `C1 {x}` and +//! `C0(a)`; the only difference is what the values of the identifiers +//! <ident for self index value> and <ident of __arg_1 index value> will +//! be in the generated code. +//! +//! `EnumNonMatchingCollapsed` deliberately provides far less information +//! than is generally available for a given pair of variants; see #15375 +//! for discussion. +//! +//! ## Static +//! +//! A static method on the types above would result in, +//! +//! ```{.text} +//! StaticStruct(<ast::VariantData of A>, Named(vec![(<ident of x>, <span of x>)])) +//! +//! StaticStruct(<ast::VariantData of B>, Unnamed(vec![<span of x>])) +//! +//! StaticEnum(<ast::EnumDef of C>, +//! vec![(<ident of C0>, <span of C0>, Unnamed(vec![<span of i32>])), +//! (<ident of C1>, <span of C1>, Named(vec![(<ident of x>, <span of x>)]))]) +//! ``` + +pub use self::StaticFields::*; +pub use self::SubstructureFields::*; +use self::StructType::*; + +use std::cell::RefCell; +use std::collections::HashSet; +use std::vec; + +use syntax::abi::Abi; +use syntax::abi; +use syntax::ast; +use syntax::ast::{EnumDef, Expr, Ident, Generics, VariantData}; +use syntax::ast_util; +use syntax::attr; +use syntax::attr::AttrMetaMethods; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::codemap::{self, DUMMY_SP}; +use syntax::codemap::Span; +use syntax::diagnostic::SpanHandler; +use syntax::util::move_map::MoveMap; +use syntax::owned_slice::OwnedSlice; +use syntax::parse::token::{intern, InternedString}; +use syntax::parse::token::special_idents; +use syntax::ptr::P; + +use self::ty::{LifetimeBounds, Path, Ptr, PtrTy, Self_, Ty}; + +pub mod ty; + +pub struct TraitDef<'a> { + /// The span for the current #[derive(Foo)] header. + pub span: Span, + + pub attributes: Vec<ast::Attribute>, + + /// Path of the trait, including any type parameters + pub path: Path<'a>, + + /// Additional bounds required of any type parameters of the type, + /// other than the current trait + pub additional_bounds: Vec<Ty<'a>>, + + /// Any extra lifetimes and/or bounds, e.g. `D: serialize::Decoder` + pub generics: LifetimeBounds<'a>, + + /// Is it an `unsafe` trait? + pub is_unsafe: bool, + + pub methods: Vec<MethodDef<'a>>, + + pub associated_types: Vec<(ast::Ident, Ty<'a>)>, +} + + +pub struct MethodDef<'a> { + /// name of the method + pub name: &'a str, + /// List of generics, e.g. `R: rand::Rng` + pub generics: LifetimeBounds<'a>, + + /// Whether there is a self argument (outer Option) i.e. whether + /// this is a static function, and whether it is a pointer (inner + /// Option) + pub explicit_self: Option<Option<PtrTy<'a>>>, + + /// Arguments other than the self argument + pub args: Vec<Ty<'a>>, + + /// Return type + pub ret_ty: Ty<'a>, + + pub attributes: Vec<ast::Attribute>, + + // Is it an `unsafe fn`? + pub is_unsafe: bool, + + pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>, +} + +/// All the data about the data structure/method being derived upon. +pub struct Substructure<'a> { + /// ident of self + pub type_ident: Ident, + /// ident of the method + pub method_ident: Ident, + /// dereferenced access to any `Self_` or `Ptr(Self_, _)` arguments + pub self_args: &'a [P<Expr>], + /// verbatim access to any other arguments + pub nonself_args: &'a [P<Expr>], + pub fields: &'a SubstructureFields<'a> +} + +/// Summary of the relevant parts of a struct/enum field. +pub struct FieldInfo<'a> { + pub span: Span, + /// None for tuple structs/normal enum variants, Some for normal + /// structs/struct enum variants. + pub name: Option<Ident>, + /// The expression corresponding to this field of `self` + /// (specifically, a reference to it). + pub self_: P<Expr>, + /// The expressions corresponding to references to this field in + /// the other `Self` arguments. + pub other: Vec<P<Expr>>, + /// The attributes on the field + pub attrs: &'a [ast::Attribute], +} + +/// Fields for a static method +pub enum StaticFields { + /// Tuple structs/enum variants like this. + Unnamed(Vec<Span>), + /// Normal structs/struct variants. + Named(Vec<(Ident, Span)>), +} + +/// A summary of the possible sets of fields. +pub enum SubstructureFields<'a> { + Struct(Vec<FieldInfo<'a>>), + /// Matching variants of the enum: variant index, ast::Variant, + /// fields: the field name is only non-`None` in the case of a struct + /// variant. + EnumMatching(usize, &'a ast::Variant, Vec<FieldInfo<'a>>), + + /// Non-matching variants of the enum, but with all state hidden from + /// the consequent code. The first component holds `Ident`s for all of + /// the `Self` arguments; the second component is a slice of all of the + /// variants for the enum itself, and the third component is a list of + /// `Ident`s bound to the variant index values for each of the actual + /// input `Self` arguments. + EnumNonMatchingCollapsed(Vec<Ident>, &'a [P<ast::Variant>], &'a [Ident]), + + /// A static method where `Self` is a struct. + StaticStruct(&'a ast::VariantData, StaticFields), + /// A static method where `Self` is an enum. + StaticEnum(&'a ast::EnumDef, Vec<(Ident, Span, StaticFields)>), +} + + + +/// Combine the values of all the fields together. The last argument is +/// all the fields of all the structures. +pub type CombineSubstructureFunc<'a> = + Box<FnMut(&mut ExtCtxt, Span, &Substructure) -> P<Expr> + 'a>; + +/// Deal with non-matching enum variants. The tuple is a list of +/// identifiers (one for each `Self` argument, which could be any of the +/// variants since they have been collapsed together) and the identifiers +/// holding the variant index value for each of the `Self` arguments. The +/// last argument is all the non-`Self` args of the method being derived. +pub type EnumNonMatchCollapsedFunc<'a> = + Box<FnMut(&mut ExtCtxt, Span, (&[Ident], &[Ident]), &[P<Expr>]) -> P<Expr> + 'a>; + +pub fn combine_substructure<'a>(f: CombineSubstructureFunc<'a>) + -> RefCell<CombineSubstructureFunc<'a>> { + RefCell::new(f) +} + +/// This method helps to extract all the type parameters referenced from a +/// type. For a type parameter `<T>`, it looks for either a `TyPath` that +/// is not global and starts with `T`, or a `TyQPath`. +fn find_type_parameters(ty: &ast::Ty, ty_param_names: &[ast::Name]) -> Vec<P<ast::Ty>> { + use syntax::visit; + + struct Visitor<'a> { + ty_param_names: &'a [ast::Name], + types: Vec<P<ast::Ty>>, + } + + impl<'a> visit::Visitor<'a> for Visitor<'a> { + fn visit_ty(&mut self, ty: &'a ast::Ty) { + match ty.node { + ast::TyPath(_, ref path) if !path.global => { + match path.segments.first() { + Some(segment) => { + if self.ty_param_names.contains(&segment.identifier.name) { + self.types.push(P(ty.clone())); + } + } + None => {} + } + } + _ => {} + } + + visit::walk_ty(self, ty) + } + } + + let mut visitor = Visitor { + ty_param_names: ty_param_names, + types: Vec::new(), + }; + + visit::Visitor::visit_ty(&mut visitor, ty); + + visitor.types +} + +impl<'a> TraitDef<'a> { + pub fn expand(&self, + cx: &mut ExtCtxt, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut FnMut(Annotatable)) + { + match *item { + Annotatable::Item(ref item) => { + let newitem = match item.node { + ast::ItemStruct(ref struct_def, ref generics) => { + self.expand_struct_def(cx, + &struct_def, + item.ident, + generics) + } + ast::ItemEnum(ref enum_def, ref generics) => { + self.expand_enum_def(cx, + enum_def, + &item.attrs, + item.ident, + generics) + } + _ => { + cx.span_err(mitem.span, + "`derive` may only be applied to structs and enums"); + return; + } + }; + // Keep the lint attributes of the previous item to control how the + // generated implementations are linted + let mut attrs = newitem.attrs.clone(); + attrs.extend(item.attrs.iter().filter(|a| { + match &a.name()[..] { + "allow" | "warn" | "deny" | "forbid" | "stable" | "unstable" => true, + _ => false, + } + }).cloned()); + push(Annotatable::Item(P(ast::Item { + attrs: attrs, + ..(*newitem).clone() + }))) + } + _ => { + cx.span_err(mitem.span, "`derive` may only be applied to structs and enums"); + } + } + } + + /// Given that we are deriving a trait `DerivedTrait` for a type like: + /// + /// ```ignore + /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { + /// a: A, + /// b: B::Item, + /// b1: <B as DeclaredTrait>::Item, + /// c1: <C as WhereTrait>::Item, + /// c2: Option<<C as WhereTrait>::Item>, + /// ... + /// } + /// ``` + /// + /// create an impl like: + /// + /// ```ignore + /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where + /// C: WhereTrait, + /// A: DerivedTrait + B1 + ... + BN, + /// B: DerivedTrait + B1 + ... + BN, + /// C: DerivedTrait + B1 + ... + BN, + /// B::Item: DerivedTrait + B1 + ... + BN, + /// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN, + /// ... + /// { + /// ... + /// } + /// ``` + /// + /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and + /// therefore does not get bound by the derived trait. + fn create_derived_impl(&self, + cx: &mut ExtCtxt, + type_ident: Ident, + generics: &Generics, + field_tys: Vec<P<ast::Ty>>, + methods: Vec<P<ast::ImplItem>>) -> P<ast::Item> { + let trait_path = self.path.to_path(cx, self.span, type_ident, generics); + + // Transform associated types from `deriving::ty::Ty` into `ast::ImplItem` + let associated_types = self.associated_types.iter().map(|&(ident, ref type_def)| { + P(ast::ImplItem { + id: ast::DUMMY_NODE_ID, + span: self.span, + ident: ident, + vis: ast::Inherited, + attrs: Vec::new(), + node: ast::ImplItemKind::Type(type_def.to_ty(cx, + self.span, + type_ident, + generics + )), + }) + }); + + let Generics { mut lifetimes, ty_params, mut where_clause } = + self.generics.to_generics(cx, self.span, type_ident, generics); + let mut ty_params = ty_params.into_vec(); + + // Copy the lifetimes + lifetimes.extend(generics.lifetimes.iter().cloned()); + + // Create the type parameters. + ty_params.extend(generics.ty_params.iter().map(|ty_param| { + // I don't think this can be moved out of the loop, since + // a TyParamBound requires an ast id + let mut bounds: Vec<_> = + // extra restrictions on the generics parameters to the type being derived upon + self.additional_bounds.iter().map(|p| { + cx.typarambound(p.to_path(cx, self.span, + type_ident, generics)) + }).collect(); + + // require the current trait + bounds.push(cx.typarambound(trait_path.clone())); + + // also add in any bounds from the declaration + for declared_bound in ty_param.bounds.iter() { + bounds.push((*declared_bound).clone()); + } + + cx.typaram(self.span, + ty_param.ident, + OwnedSlice::from_vec(bounds), + None) + })); + + // and similarly for where clauses + where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { + match *clause { + ast::WherePredicate::BoundPredicate(ref wb) => { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + span: self.span, + bound_lifetimes: wb.bound_lifetimes.clone(), + bounded_ty: wb.bounded_ty.clone(), + bounds: OwnedSlice::from_vec(wb.bounds.iter().cloned().collect()) + }) + } + ast::WherePredicate::RegionPredicate(ref rb) => { + ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + span: self.span, + lifetime: rb.lifetime, + bounds: rb.bounds.iter().cloned().collect() + }) + } + ast::WherePredicate::EqPredicate(ref we) => { + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { + id: ast::DUMMY_NODE_ID, + span: self.span, + path: we.path.clone(), + ty: we.ty.clone() + }) + } + } + })); + + if !ty_params.is_empty() { + let ty_param_names: Vec<ast::Name> = ty_params.iter() + .map(|ty_param| ty_param.ident.name) + .collect(); + + let mut processed_field_types = HashSet::new(); + for field_ty in field_tys { + let tys = find_type_parameters(&*field_ty, &ty_param_names); + + for ty in tys { + // if we have already handled this type, skip it + if let ast::TyPath(_, ref p) = ty.node { + if p.segments.len() == 1 + && ty_param_names.contains(&p.segments[0].identifier.name) + || processed_field_types.contains(&p.segments) { + continue; + }; + processed_field_types.insert(p.segments.clone()); + } + let mut bounds: Vec<_> = self.additional_bounds.iter().map(|p| { + cx.typarambound(p.to_path(cx, self.span, type_ident, generics)) + }).collect(); + + // require the current trait + bounds.push(cx.typarambound(trait_path.clone())); + + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_lifetimes: vec![], + bounded_ty: ty, + bounds: OwnedSlice::from_vec(bounds), + }; + + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); + } + } + } + + let trait_generics = Generics { + lifetimes: lifetimes, + ty_params: OwnedSlice::from_vec(ty_params), + where_clause: where_clause + }; + + // Create the reference to the trait. + let trait_ref = cx.trait_ref(trait_path); + + // Create the type parameters on the `self` path. + let self_ty_params = generics.ty_params.map(|ty_param| { + cx.ty_ident(self.span, ty_param.ident) + }); + + let self_lifetimes: Vec<ast::Lifetime> = + generics.lifetimes + .iter() + .map(|ld| ld.lifetime) + .collect(); + + // Create the type of `self`. + let self_type = cx.ty_path( + cx.path_all(self.span, false, vec!( type_ident ), self_lifetimes, + self_ty_params.into_vec(), Vec::new())); + + let attr = cx.attribute( + self.span, + cx.meta_word(self.span, + InternedString::new("automatically_derived"))); + // Just mark it now since we know that it'll end up used downstream + attr::mark_used(&attr); + let opt_trait_ref = Some(trait_ref); + let ident = ast_util::impl_pretty_name(&opt_trait_ref, Some(&*self_type)); + let unused_qual = cx.attribute( + self.span, + cx.meta_list(self.span, + InternedString::new("allow"), + vec![cx.meta_word(self.span, + InternedString::new("unused_qualifications"))])); + let mut a = vec![attr, unused_qual]; + a.extend(self.attributes.iter().cloned()); + + let unsafety = if self.is_unsafe { + ast::Unsafety::Unsafe + } else { + ast::Unsafety::Normal + }; + + cx.item( + self.span, + ident, + a, + ast::ItemImpl(unsafety, + ast::ImplPolarity::Positive, + trait_generics, + opt_trait_ref, + self_type, + methods.into_iter().chain(associated_types).collect())) + } + + fn expand_struct_def(&self, + cx: &mut ExtCtxt, + struct_def: &'a VariantData, + type_ident: Ident, + generics: &Generics) -> P<ast::Item> { + let field_tys: Vec<P<ast::Ty>> = struct_def.fields().iter() + .map(|field| field.node.ty.clone()) + .collect(); + + let methods = self.methods.iter().map(|method_def| { + let (explicit_self, self_args, nonself_args, tys) = + method_def.split_self_nonself_args( + cx, self, type_ident, generics); + + let body = if method_def.is_static() { + method_def.expand_static_struct_method_body( + cx, + self, + struct_def, + type_ident, + &self_args[..], + &nonself_args[..]) + } else { + method_def.expand_struct_method_body(cx, + self, + struct_def, + type_ident, + &self_args[..], + &nonself_args[..]) + }; + + method_def.create_method(cx, + self, + type_ident, + generics, + abi::Rust, + explicit_self, + tys, + body) + }).collect(); + + self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + } + + fn expand_enum_def(&self, + cx: &mut ExtCtxt, + enum_def: &'a EnumDef, + type_attrs: &[ast::Attribute], + type_ident: Ident, + generics: &Generics) -> P<ast::Item> { + let mut field_tys = Vec::new(); + + for variant in &enum_def.variants { + field_tys.extend(variant.node.data.fields().iter() + .map(|field| field.node.ty.clone())); + } + + let methods = self.methods.iter().map(|method_def| { + let (explicit_self, self_args, nonself_args, tys) = + method_def.split_self_nonself_args(cx, self, + type_ident, generics); + + let body = if method_def.is_static() { + method_def.expand_static_enum_method_body( + cx, + self, + enum_def, + type_ident, + &self_args[..], + &nonself_args[..]) + } else { + method_def.expand_enum_method_body(cx, + self, + enum_def, + type_attrs, + type_ident, + self_args, + &nonself_args[..]) + }; + + method_def.create_method(cx, + self, + type_ident, + generics, + abi::Rust, + explicit_self, + tys, + body) + }).collect(); + + self.create_derived_impl(cx, type_ident, generics, field_tys, methods) + } +} + +fn find_repr_type_name(diagnostic: &SpanHandler, + type_attrs: &[ast::Attribute]) -> &'static str { + let mut repr_type_name = "i32"; + for a in type_attrs { + for r in &attr::find_repr_attrs(diagnostic, a) { + repr_type_name = match *r { + attr::ReprAny | attr::ReprPacked | attr::ReprSimd => continue, + attr::ReprExtern => "i32", + + attr::ReprInt(_, attr::SignedInt(ast::TyIs)) => "isize", + attr::ReprInt(_, attr::SignedInt(ast::TyI8)) => "i8", + attr::ReprInt(_, attr::SignedInt(ast::TyI16)) => "i16", + attr::ReprInt(_, attr::SignedInt(ast::TyI32)) => "i32", + attr::ReprInt(_, attr::SignedInt(ast::TyI64)) => "i64", + + attr::ReprInt(_, attr::UnsignedInt(ast::TyUs)) => "usize", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU8)) => "u8", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU16)) => "u16", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU32)) => "u32", + attr::ReprInt(_, attr::UnsignedInt(ast::TyU64)) => "u64", + } + } + } + repr_type_name +} + +impl<'a> MethodDef<'a> { + fn call_substructure_method(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>], + fields: &SubstructureFields) + -> P<Expr> { + let substructure = Substructure { + type_ident: type_ident, + method_ident: cx.ident_of(self.name), + self_args: self_args, + nonself_args: nonself_args, + fields: fields + }; + let mut f = self.combine_substructure.borrow_mut(); + let f: &mut CombineSubstructureFunc = &mut *f; + f(cx, trait_.span, &substructure) + } + + fn get_ret_ty(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + generics: &Generics, + type_ident: Ident) + -> P<ast::Ty> { + self.ret_ty.to_ty(cx, trait_.span, type_ident, generics) + } + + fn is_static(&self) -> bool { + self.explicit_self.is_none() + } + + fn split_self_nonself_args(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + type_ident: Ident, + generics: &Generics) + -> (ast::ExplicitSelf, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) { + + let mut self_args = Vec::new(); + let mut nonself_args = Vec::new(); + let mut arg_tys = Vec::new(); + let mut nonstatic = false; + + let ast_explicit_self = match self.explicit_self { + Some(ref self_ptr) => { + let (self_expr, explicit_self) = + ty::get_explicit_self(cx, trait_.span, self_ptr); + + self_args.push(self_expr); + nonstatic = true; + + explicit_self + } + None => codemap::respan(trait_.span, ast::SelfStatic), + }; + + for (i, ty) in self.args.iter().enumerate() { + let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics); + let ident = cx.ident_of(&format!("__arg_{}", i)); + arg_tys.push((ident, ast_ty)); + + let arg_expr = cx.expr_ident(trait_.span, ident); + + match *ty { + // for static methods, just treat any Self + // arguments as a normal arg + Self_ if nonstatic => { + self_args.push(arg_expr); + } + Ptr(ref ty, _) if **ty == Self_ && nonstatic => { + self_args.push(cx.expr_deref(trait_.span, arg_expr)) + } + _ => { + nonself_args.push(arg_expr); + } + } + } + + (ast_explicit_self, self_args, nonself_args, arg_tys) + } + + fn create_method(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + type_ident: Ident, + generics: &Generics, + abi: Abi, + explicit_self: ast::ExplicitSelf, + arg_types: Vec<(Ident, P<ast::Ty>)> , + body: P<Expr>) -> P<ast::ImplItem> { + // create the generics that aren't for Self + let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics); + + let self_arg = match explicit_self.node { + ast::SelfStatic => None, + // creating fresh self id + _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, special_idents::self_)) + }; + let args = { + let args = arg_types.into_iter().map(|(name, ty)| { + cx.arg(trait_.span, name, ty) + }); + self_arg.into_iter().chain(args).collect() + }; + + let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); + + let method_ident = cx.ident_of(self.name); + let fn_decl = cx.fn_decl(args, ret_type); + let body_block = cx.block_expr(body); + + let unsafety = if self.is_unsafe { + ast::Unsafety::Unsafe + } else { + ast::Unsafety::Normal + }; + + // Create the method. + P(ast::ImplItem { + id: ast::DUMMY_NODE_ID, + attrs: self.attributes.clone(), + span: trait_.span, + vis: ast::Inherited, + ident: method_ident, + node: ast::ImplItemKind::Method(ast::MethodSig { + generics: fn_generics, + abi: abi, + explicit_self: explicit_self, + unsafety: unsafety, + constness: ast::Constness::NotConst, + decl: fn_decl + }, body_block) + }) + } + + /// ```ignore + /// #[derive(PartialEq)] + /// struct A { x: i32, y: i32 } + /// + /// // equivalent to: + /// impl PartialEq for A { + /// fn eq(&self, __arg_1: &A) -> bool { + /// match *self { + /// A {x: ref __self_0_0, y: ref __self_0_1} => { + /// match *__arg_1 { + /// A {x: ref __self_1_0, y: ref __self_1_1} => { + /// __self_0_0.eq(__self_1_0) && __self_0_1.eq(__self_1_1) + /// } + /// } + /// } + /// } + /// } + /// } + /// ``` + fn expand_struct_method_body<'b>(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef<'b>, + struct_def: &'b VariantData, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>]) + -> P<Expr> { + + let mut raw_fields = Vec::new(); // Vec<[fields of self], + // [fields of next Self arg], [etc]> + let mut patterns = Vec::new(); + for i in 0..self_args.len() { + let struct_path= cx.path(DUMMY_SP, vec!( type_ident )); + let (pat, ident_expr) = + trait_.create_struct_pattern(cx, + struct_path, + struct_def, + &format!("__self_{}", + i), + ast::MutImmutable); + patterns.push(pat); + raw_fields.push(ident_expr); + } + + // transpose raw_fields + let fields = if !raw_fields.is_empty() { + let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter()); + let first_field = raw_fields.next().unwrap(); + let mut other_fields: Vec<vec::IntoIter<_>> + = raw_fields.collect(); + first_field.map(|(span, opt_id, field, attrs)| { + FieldInfo { + span: span, + name: opt_id, + self_: field, + other: other_fields.iter_mut().map(|l| { + match l.next().unwrap() { + (_, _, ex, _) => ex + } + }).collect(), + attrs: attrs, + } + }).collect() + } else { + cx.span_bug(trait_.span, + "no self arguments to non-static method in generic \ + `derive`") + }; + + // body of the inner most destructuring match + let mut body = self.call_substructure_method( + cx, + trait_, + type_ident, + self_args, + nonself_args, + &Struct(fields)); + + // make a series of nested matches, to destructure the + // structs. This is actually right-to-left, but it shouldn't + // matter. + for (arg_expr, pat) in self_args.iter().zip(patterns) { + body = cx.expr_match(trait_.span, arg_expr.clone(), + vec!( cx.arm(trait_.span, vec!(pat.clone()), body) )) + } + body + } + + fn expand_static_struct_method_body(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + struct_def: &VariantData, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>]) + -> P<Expr> { + let summary = trait_.summarise_struct(cx, struct_def); + + self.call_substructure_method(cx, + trait_, + type_ident, + self_args, nonself_args, + &StaticStruct(struct_def, summary)) + } + + /// ```ignore + /// #[derive(PartialEq)] + /// enum A { + /// A1, + /// A2(i32) + /// } + /// + /// // is equivalent to + /// + /// impl PartialEq for A { + /// fn eq(&self, __arg_1: &A) -> ::bool { + /// match (&*self, &*__arg_1) { + /// (&A1, &A1) => true, + /// (&A2(ref __self_0), + /// &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)), + /// _ => { + /// let __self_vi = match *self { A1(..) => 0, A2(..) => 1 }; + /// let __arg_1_vi = match *__arg_1 { A1(..) => 0, A2(..) => 1 }; + /// false + /// } + /// } + /// } + /// } + /// ``` + /// + /// (Of course `__self_vi` and `__arg_1_vi` are unused for + /// `PartialEq`, and those subcomputations will hopefully be removed + /// as their results are unused. The point of `__self_vi` and + /// `__arg_1_vi` is for `PartialOrd`; see #15503.) + fn expand_enum_method_body<'b>(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef<'b>, + enum_def: &'b EnumDef, + type_attrs: &[ast::Attribute], + type_ident: Ident, + self_args: Vec<P<Expr>>, + nonself_args: &[P<Expr>]) + -> P<Expr> { + self.build_enum_match_tuple( + cx, trait_, enum_def, type_attrs, type_ident, self_args, nonself_args) + } + + + /// Creates a match for a tuple of all `self_args`, where either all + /// variants match, or it falls into a catch-all for when one variant + /// does not match. + + /// There are N + 1 cases because is a case for each of the N + /// variants where all of the variants match, and one catch-all for + /// when one does not match. + + /// As an optimization we generate code which checks whether all variants + /// match first which makes llvm see that C-like enums can be compiled into + /// a simple equality check (for PartialEq). + + /// The catch-all handler is provided access the variant index values + /// for each of the self-args, carried in precomputed variables. + + /// ```{.text} + /// let __self0_vi = unsafe { + /// std::intrinsics::discriminant_value(&self) } as i32; + /// let __self1_vi = unsafe { + /// std::intrinsics::discriminant_value(&__arg1) } as i32; + /// let __self2_vi = unsafe { + /// std::intrinsics::discriminant_value(&__arg2) } as i32; + /// + /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { + /// match (...) { + /// (Variant1, Variant1, ...) => Body1 + /// (Variant2, Variant2, ...) => Body2, + /// ... + /// _ => ::core::intrinsics::unreachable() + /// } + /// } + /// else { + /// ... // catch-all remainder can inspect above variant index values. + /// } + /// ``` + fn build_enum_match_tuple<'b>( + &self, + cx: &mut ExtCtxt, + trait_: &TraitDef<'b>, + enum_def: &'b EnumDef, + type_attrs: &[ast::Attribute], + type_ident: Ident, + self_args: Vec<P<Expr>>, + nonself_args: &[P<Expr>]) -> P<Expr> { + + let sp = trait_.span; + let variants = &enum_def.variants; + + let self_arg_names = self_args.iter().enumerate() + .map(|(arg_count, _self_arg)| { + if arg_count == 0 { + "__self".to_string() + } else { + format!("__arg_{}", arg_count) + } + }) + .collect::<Vec<String>>(); + + let self_arg_idents = self_arg_names.iter() + .map(|name|cx.ident_of(&name[..])) + .collect::<Vec<ast::Ident>>(); + + // The `vi_idents` will be bound, solely in the catch-all, to + // a series of let statements mapping each self_arg to an int + // value corresponding to its discriminant. + let vi_idents: Vec<ast::Ident> = self_arg_names.iter() + .map(|name| { let vi_suffix = format!("{}_vi", &name[..]); + cx.ident_of(&vi_suffix[..]) }) + .collect::<Vec<ast::Ident>>(); + + // Builds, via callback to call_substructure_method, the + // delegated expression that handles the catch-all case, + // using `__variants_tuple` to drive logic if necessary. + let catch_all_substructure = EnumNonMatchingCollapsed( + self_arg_idents, &variants[..], &vi_idents[..]); + + // These arms are of the form: + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2 + // ... + // where each tuple has length = self_args.len() + let mut match_arms: Vec<ast::Arm> = variants.iter().enumerate() + .map(|(index, variant)| { + let mk_self_pat = |cx: &mut ExtCtxt, self_arg_name: &str| { + let (p, idents) = trait_.create_enum_variant_pattern(cx, type_ident, + &**variant, + self_arg_name, + ast::MutImmutable); + (cx.pat(sp, ast::PatRegion(p, ast::MutImmutable)), idents) + }; + + // A single arm has form (&VariantK, &VariantK, ...) => BodyK + // (see "Final wrinkle" note below for why.) + let mut subpats = Vec::with_capacity(self_arg_names.len()); + let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1); + let first_self_pat_idents = { + let (p, idents) = mk_self_pat(cx, &self_arg_names[0]); + subpats.push(p); + idents + }; + for self_arg_name in &self_arg_names[1..] { + let (p, idents) = mk_self_pat(cx, &self_arg_name[..]); + subpats.push(p); + self_pats_idents.push(idents); + } + + // Here is the pat = `(&VariantK, &VariantK, ...)` + let single_pat = cx.pat_tuple(sp, subpats); + + // For the BodyK, we need to delegate to our caller, + // passing it an EnumMatching to indicate which case + // we are in. + + // All of the Self args have the same variant in these + // cases. So we transpose the info in self_pats_idents + // to gather the getter expressions together, in the + // form that EnumMatching expects. + + // The transposition is driven by walking across the + // arg fields of the variant for the first self pat. + let field_tuples = first_self_pat_idents.into_iter().enumerate() + // For each arg field of self, pull out its getter expr ... + .map(|(field_index, (sp, opt_ident, self_getter_expr, attrs))| { + // ... but FieldInfo also wants getter expr + // for matching other arguments of Self type; + // so walk across the *other* self_pats_idents + // and pull out getter for same field in each + // of them (using `field_index` tracked above). + // That is the heart of the transposition. + let others = self_pats_idents.iter().map(|fields| { + let (_, _opt_ident, ref other_getter_expr, _) = + fields[field_index]; + + // All Self args have same variant, so + // opt_idents are the same. (Assert + // here to make it self-evident that + // it is okay to ignore `_opt_ident`.) + assert!(opt_ident == _opt_ident); + + other_getter_expr.clone() + }).collect::<Vec<P<Expr>>>(); + + FieldInfo { span: sp, + name: opt_ident, + self_: self_getter_expr, + other: others, + attrs: attrs, + } + }).collect::<Vec<FieldInfo>>(); + + // Now, for some given VariantK, we have built up + // expressions for referencing every field of every + // Self arg, assuming all are instances of VariantK. + // Build up code associated with such a case. + let substructure = EnumMatching(index, + &**variant, + field_tuples); + let arm_expr = self.call_substructure_method( + cx, trait_, type_ident, &self_args[..], nonself_args, + &substructure); + + cx.arm(sp, vec![single_pat], arm_expr) + }).collect(); + // We will usually need the catch-all after matching the + // tuples `(VariantK, VariantK, ...)` for each VariantK of the + // enum. But: + // + // * when there is only one Self arg, the arms above suffice + // (and the deriving we call back into may not be prepared to + // handle EnumNonMatchCollapsed), and, + // + // * when the enum has only one variant, the single arm that + // is already present always suffices. + // + // * In either of the two cases above, if we *did* add a + // catch-all `_` match, it would trigger the + // unreachable-pattern error. + // + if variants.len() > 1 && self_args.len() > 1 { + // Build a series of let statements mapping each self_arg + // to its discriminant value. If this is a C-style enum + // with a specific repr type, then casts the values to + // that type. Otherwise casts to `i32` (the default repr + // type). + // + // i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving + // with three Self args, builds three statements: + // + // ``` + // let __self0_vi = unsafe { + // std::intrinsics::discriminant_value(&self) } as i32; + // let __self1_vi = unsafe { + // std::intrinsics::discriminant_value(&__arg1) } as i32; + // let __self2_vi = unsafe { + // std::intrinsics::discriminant_value(&__arg2) } as i32; + // ``` + let mut index_let_stmts: Vec<P<ast::Stmt>> = Vec::new(); + + //We also build an expression which checks whether all discriminants are equal + // discriminant_test = __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... + let mut discriminant_test = cx.expr_bool(sp, true); + + let target_type_name = + find_repr_type_name(&cx.parse_sess.span_diagnostic, type_attrs); + + let mut first_ident = None; + for (&ident, self_arg) in vi_idents.iter().zip(&self_args) { + let path = cx.std_path(&["intrinsics", "discriminant_value"]); + let call = cx.expr_call_global( + sp, path, vec![cx.expr_addr_of(sp, self_arg.clone())]); + let variant_value = cx.expr_block(P(ast::Block { + stmts: vec![], + expr: Some(call), + id: ast::DUMMY_NODE_ID, + rules: ast::UnsafeBlock(ast::CompilerGenerated), + span: sp })); + + let target_ty = cx.ty_ident(sp, cx.ident_of(target_type_name)); + let variant_disr = cx.expr_cast(sp, variant_value, target_ty); + let let_stmt = cx.stmt_let(sp, false, ident, variant_disr); + index_let_stmts.push(let_stmt); + + match first_ident { + Some(first) => { + let first_expr = cx.expr_ident(sp, first); + let id = cx.expr_ident(sp, ident); + let test = cx.expr_binary(sp, ast::BiEq, first_expr, id); + discriminant_test = cx.expr_binary(sp, ast::BiAnd, discriminant_test, test) + } + None => { + first_ident = Some(ident); + } + } + } + + let arm_expr = self.call_substructure_method( + cx, trait_, type_ident, &self_args[..], nonself_args, + &catch_all_substructure); + + //Since we know that all the arguments will match if we reach the match expression we + //add the unreachable intrinsics as the result of the catch all which should help llvm + //in optimizing it + let path = cx.std_path(&["intrinsics", "unreachable"]); + let call = cx.expr_call_global( + sp, path, vec![]); + let unreachable = cx.expr_block(P(ast::Block { + stmts: vec![], + expr: Some(call), + id: ast::DUMMY_NODE_ID, + rules: ast::UnsafeBlock(ast::CompilerGenerated), + span: sp })); + match_arms.push(cx.arm(sp, vec![cx.pat_wild(sp)], unreachable)); + + // Final wrinkle: the self_args are expressions that deref + // down to desired l-values, but we cannot actually deref + // them when they are fed as r-values into a tuple + // expression; here add a layer of borrowing, turning + // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. + let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg)); + let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args)); + + //Lastly we create an expression which branches on all discriminants being equal + // if discriminant_test { + // match (...) { + // (Variant1, Variant1, ...) => Body1 + // (Variant2, Variant2, ...) => Body2, + // ... + // _ => ::core::intrinsics::unreachable() + // } + // } + // else { + // <delegated expression referring to __self0_vi, et al.> + // } + let all_match = cx.expr_match(sp, match_arg, match_arms); + let arm_expr = cx.expr_if(sp, discriminant_test, all_match, Some(arm_expr)); + cx.expr_block( + cx.block_all(sp, index_let_stmts, Some(arm_expr))) + } else if variants.is_empty() { + // As an additional wrinkle, For a zero-variant enum A, + // currently the compiler + // will accept `fn (a: &Self) { match *a { } }` + // but rejects `fn (a: &Self) { match (&*a,) { } }` + // as well as `fn (a: &Self) { match ( *a,) { } }` + // + // This means that the strategy of building up a tuple of + // all Self arguments fails when Self is a zero variant + // enum: rustc rejects the expanded program, even though + // the actual code tends to be impossible to execute (at + // least safely), according to the type system. + // + // The most expedient fix for this is to just let the + // code fall through to the catch-all. But even this is + // error-prone, since the catch-all as defined above would + // generate code like this: + // + // _ => { let __self0 = match *self { }; + // let __self1 = match *__arg_0 { }; + // <catch-all-expr> } + // + // Which is yields bindings for variables which type + // inference cannot resolve to unique types. + // + // One option to the above might be to add explicit type + // annotations. But the *only* reason to go down that path + // would be to try to make the expanded output consistent + // with the case when the number of enum variants >= 1. + // + // That just isn't worth it. In fact, trying to generate + // sensible code for *any* deriving on a zero-variant enum + // does not make sense. But at the same time, for now, we + // do not want to cause a compile failure just because the + // user happened to attach a deriving to their + // zero-variant enum. + // + // Instead, just generate a failing expression for the + // zero variant case, skipping matches and also skipping + // delegating back to the end user code entirely. + // + // (See also #4499 and #12609; note that some of the + // discussions there influence what choice we make here; + // e.g. if we feature-gate `match x { ... }` when x refers + // to an uninhabited type (e.g. a zero-variant enum or a + // type holding such an enum), but do not feature-gate + // zero-variant enums themselves, then attempting to + // derive Debug on such a type could here generate code + // that needs the feature gate enabled.) + + cx.expr_unreachable(sp) + } + else { + + // Final wrinkle: the self_args are expressions that deref + // down to desired l-values, but we cannot actually deref + // them when they are fed as r-values into a tuple + // expression; here add a layer of borrowing, turning + // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. + let borrowed_self_args = self_args.move_map(|self_arg| cx.expr_addr_of(sp, self_arg)); + let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args)); + cx.expr_match(sp, match_arg, match_arms) + } + } + + fn expand_static_enum_method_body(&self, + cx: &mut ExtCtxt, + trait_: &TraitDef, + enum_def: &EnumDef, + type_ident: Ident, + self_args: &[P<Expr>], + nonself_args: &[P<Expr>]) + -> P<Expr> { + let summary = enum_def.variants.iter().map(|v| { + let ident = v.node.name; + let summary = trait_.summarise_struct(cx, &v.node.data); + (ident, v.span, summary) + }).collect(); + self.call_substructure_method(cx, trait_, type_ident, + self_args, nonself_args, + &StaticEnum(enum_def, summary)) + } +} + +#[derive(PartialEq)] // dogfooding! +enum StructType { + Unknown, Record, Tuple +} + +// general helper methods. +impl<'a> TraitDef<'a> { + fn set_expn_info(&self, + cx: &mut ExtCtxt, + mut to_set: Span) -> Span { + let trait_name = match self.path.path.last() { + None => cx.span_bug(self.span, "trait with empty path in generic `derive`"), + Some(name) => *name + }; + to_set.expn_id = cx.codemap().record_expansion(codemap::ExpnInfo { + call_site: to_set, + callee: codemap::NameAndSpan { + format: codemap::MacroAttribute(intern(&format!("derive({})", trait_name))), + span: Some(self.span), + allow_internal_unstable: false, + } + }); + to_set + } + + fn summarise_struct(&self, + cx: &mut ExtCtxt, + struct_def: &VariantData) -> StaticFields { + let mut named_idents = Vec::new(); + let mut just_spans = Vec::new(); + for field in struct_def.fields(){ + let sp = self.set_expn_info(cx, field.span); + match field.node.kind { + ast::NamedField(ident, _) => named_idents.push((ident, sp)), + ast::UnnamedField(..) => just_spans.push(sp), + } + } + + match (just_spans.is_empty(), named_idents.is_empty()) { + (false, false) => cx.span_bug(self.span, + "a struct with named and unnamed \ + fields in generic `derive`"), + // named fields + (_, false) => Named(named_idents), + // tuple structs (includes empty structs) + (_, _) => Unnamed(just_spans) + } + } + + fn create_subpatterns(&self, + cx: &mut ExtCtxt, + field_paths: Vec<ast::SpannedIdent> , + mutbl: ast::Mutability) + -> Vec<P<ast::Pat>> { + field_paths.iter().map(|path| { + cx.pat(path.span, + ast::PatIdent(ast::BindByRef(mutbl), (*path).clone(), None)) + }).collect() + } + + fn create_struct_pattern(&self, + cx: &mut ExtCtxt, + struct_path: ast::Path, + struct_def: &'a VariantData, + prefix: &str, + mutbl: ast::Mutability) + -> (P<ast::Pat>, Vec<(Span, Option<Ident>, + P<Expr>, + &'a [ast::Attribute])>) { + if struct_def.fields().is_empty() { + return (cx.pat_enum(self.span, struct_path, vec![]), vec![]); + } + + let mut paths = Vec::new(); + let mut ident_expr = Vec::new(); + let mut struct_type = Unknown; + + for (i, struct_field) in struct_def.fields().iter().enumerate() { + let sp = self.set_expn_info(cx, struct_field.span); + let opt_id = match struct_field.node.kind { + ast::NamedField(ident, _) if (struct_type == Unknown || + struct_type == Record) => { + struct_type = Record; + Some(ident) + } + ast::UnnamedField(..) if (struct_type == Unknown || + struct_type == Tuple) => { + struct_type = Tuple; + None + } + _ => { + cx.span_bug(sp, "a struct with named and unnamed fields in `derive`"); + } + }; + let ident = cx.ident_of(&format!("{}_{}", prefix, i)); + paths.push(codemap::Spanned{span: sp, node: ident}); + let val = cx.expr( + sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(cx.path_ident(sp,ident))))); + ident_expr.push((sp, opt_id, val, &struct_field.node.attrs[..])); + } + + let subpats = self.create_subpatterns(cx, paths, mutbl); + + // struct_type is definitely not Unknown, since struct_def.fields + // must be nonempty to reach here + let pattern = if struct_type == Record { + let field_pats = subpats.into_iter().zip(&ident_expr) + .map(|(pat, &(_, id, _, _))| { + // id is guaranteed to be Some + codemap::Spanned { + span: pat.span, + node: ast::FieldPat { ident: id.unwrap(), pat: pat, is_shorthand: false }, + } + }).collect(); + cx.pat_struct(self.span, struct_path, field_pats) + } else { + cx.pat_enum(self.span, struct_path, subpats) + }; + + (pattern, ident_expr) + } + + fn create_enum_variant_pattern(&self, + cx: &mut ExtCtxt, + enum_ident: ast::Ident, + variant: &'a ast::Variant, + prefix: &str, + mutbl: ast::Mutability) + -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) { + let variant_ident = variant.node.name; + let variant_path = cx.path(variant.span, vec![enum_ident, variant_ident]); + self.create_struct_pattern(cx, variant_path, &variant.node.data, prefix, mutbl) + } +} + +/* helpful premade recipes */ + +/// Fold the fields. `use_foldl` controls whether this is done +/// left-to-right (`true`) or right-to-left (`false`). +pub fn cs_fold<F>(use_foldl: bool, + mut f: F, + base: P<Expr>, + mut enum_nonmatch_f: EnumNonMatchCollapsedFunc, + cx: &mut ExtCtxt, + trait_span: Span, + substructure: &Substructure) + -> P<Expr> where + F: FnMut(&mut ExtCtxt, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, +{ + match *substructure.fields { + EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => { + if use_foldl { + all_fields.iter().fold(base, |old, field| { + f(cx, + field.span, + old, + field.self_.clone(), + &field.other) + }) + } else { + all_fields.iter().rev().fold(base, |old, field| { + f(cx, + field.span, + old, + field.self_.clone(), + &field.other) + }) + } + }, + EnumNonMatchingCollapsed(ref all_args, _, tuple) => + enum_nonmatch_f(cx, trait_span, (&all_args[..], tuple), + substructure.nonself_args), + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, "static function in `derive`") + } + } +} + + +/// Call the method that is being derived on all the fields, and then +/// process the collected results. i.e. +/// +/// ```ignore +/// f(cx, span, vec![self_1.method(__arg_1_1, __arg_2_1), +/// self_2.method(__arg_1_2, __arg_2_2)]) +/// ``` +#[inline] +pub fn cs_same_method<F>(f: F, + mut enum_nonmatch_f: EnumNonMatchCollapsedFunc, + cx: &mut ExtCtxt, + trait_span: Span, + substructure: &Substructure) + -> P<Expr> where + F: FnOnce(&mut ExtCtxt, Span, Vec<P<Expr>>) -> P<Expr>, +{ + match *substructure.fields { + EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => { + // call self_n.method(other_1_n, other_2_n, ...) + let called = all_fields.iter().map(|field| { + cx.expr_method_call(field.span, + field.self_.clone(), + substructure.method_ident, + field.other.iter() + .map(|e| cx.expr_addr_of(field.span, e.clone())) + .collect()) + }).collect(); + + f(cx, trait_span, called) + }, + EnumNonMatchingCollapsed(ref all_self_args, _, tuple) => + enum_nonmatch_f(cx, trait_span, (&all_self_args[..], tuple), + substructure.nonself_args), + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, "static function in `derive`") + } + } +} diff --git a/src/libsyntax_ext/deriving/generic/ty.rs b/src/libsyntax_ext/deriving/generic/ty.rs new file mode 100644 index 00000000000..0c4a81361ae --- /dev/null +++ b/src/libsyntax_ext/deriving/generic/ty.rs @@ -0,0 +1,284 @@ +// 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 <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. + +//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use +//! when specifying impls to be derived. + +pub use self::PtrTy::*; +pub use self::Ty::*; + +use syntax::ast; +use syntax::ast::{Expr,Generics,Ident}; +use syntax::ext::base::ExtCtxt; +use syntax::ext::build::AstBuilder; +use syntax::codemap::{Span,respan}; +use syntax::owned_slice::OwnedSlice; +use syntax::parse::token::special_idents; +use syntax::ptr::P; + +/// The types of pointers +#[derive(Clone, Eq, PartialEq)] +#[allow(dead_code)] +pub enum PtrTy<'a> { + /// &'lifetime mut + Borrowed(Option<&'a str>, ast::Mutability), + /// *mut + Raw(ast::Mutability), +} + +/// A path, e.g. `::std::option::Option::<i32>` (global). Has support +/// for type parameters and a lifetime. +#[derive(Clone, Eq, PartialEq)] +pub struct Path<'a> { + pub path: Vec<&'a str> , + pub lifetime: Option<&'a str>, + pub params: Vec<Box<Ty<'a>>>, + pub global: bool, +} + +impl<'a> Path<'a> { + pub fn new<'r>(path: Vec<&'r str> ) -> Path<'r> { + Path::new_(path, None, Vec::new(), true) + } + pub fn new_local<'r>(path: &'r str) -> Path<'r> { + Path::new_(vec!( path ), None, Vec::new(), false) + } + pub fn new_<'r>(path: Vec<&'r str> , + lifetime: Option<&'r str>, + params: Vec<Box<Ty<'r>>>, + global: bool) + -> Path<'r> { + Path { + path: path, + lifetime: lifetime, + params: params, + global: global + } + } + + pub fn to_ty(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> P<ast::Ty> { + cx.ty_path(self.to_path(cx, span, self_ty, self_generics)) + } + pub fn to_path(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> ast::Path { + let idents = self.path.iter().map(|s| cx.ident_of(*s)).collect(); + let lt = mk_lifetimes(cx, span, &self.lifetime); + let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect(); + + cx.path_all(span, self.global, idents, lt, tys, Vec::new()) + } +} + +/// A type. Supports pointers, Self, and literals +#[derive(Clone, Eq, PartialEq)] +pub enum Ty<'a> { + Self_, + /// &/Box/ Ty + Ptr(Box<Ty<'a>>, PtrTy<'a>), + /// mod::mod::Type<[lifetime], [Params...]>, including a plain type + /// parameter, and things like `i32` + Literal(Path<'a>), + /// includes unit + Tuple(Vec<Ty<'a>> ) +} + +pub fn borrowed_ptrty<'r>() -> PtrTy<'r> { + Borrowed(None, ast::MutImmutable) +} +pub fn borrowed<'r>(ty: Box<Ty<'r>>) -> Ty<'r> { + Ptr(ty, borrowed_ptrty()) +} + +pub fn borrowed_explicit_self<'r>() -> Option<Option<PtrTy<'r>>> { + Some(Some(borrowed_ptrty())) +} + +pub fn borrowed_self<'r>() -> Ty<'r> { + borrowed(Box::new(Self_)) +} + +pub fn nil_ty<'r>() -> Ty<'r> { + Tuple(Vec::new()) +} + +fn mk_lifetime(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Option<ast::Lifetime> { + match *lt { + Some(ref s) => Some(cx.lifetime(span, cx.ident_of(*s).name)), + None => None + } +} + +fn mk_lifetimes(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Vec<ast::Lifetime> { + match *lt { + Some(ref s) => vec!(cx.lifetime(span, cx.ident_of(*s).name)), + None => vec!() + } +} + +impl<'a> Ty<'a> { + pub fn to_ty(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> P<ast::Ty> { + match *self { + Ptr(ref ty, ref ptr) => { + let raw_ty = ty.to_ty(cx, span, self_ty, self_generics); + match *ptr { + Borrowed(ref lt, mutbl) => { + let lt = mk_lifetime(cx, span, lt); + cx.ty_rptr(span, raw_ty, lt, mutbl) + } + Raw(mutbl) => cx.ty_ptr(span, raw_ty, mutbl) + } + } + Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) } + Self_ => { + cx.ty_path(self.to_path(cx, span, self_ty, self_generics)) + } + Tuple(ref fields) => { + let ty = ast::TyTup(fields.iter() + .map(|f| f.to_ty(cx, span, self_ty, self_generics)) + .collect()); + cx.ty(span, ty) + } + } + } + + pub fn to_path(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> ast::Path { + match *self { + Self_ => { + let self_params = self_generics.ty_params.map(|ty_param| { + cx.ty_ident(span, ty_param.ident) + }); + let lifetimes = self_generics.lifetimes.iter() + .map(|d| d.lifetime) + .collect(); + + cx.path_all(span, false, vec!(self_ty), lifetimes, + self_params.into_vec(), Vec::new()) + } + Literal(ref p) => { + p.to_path(cx, span, self_ty, self_generics) + } + Ptr(..) => { cx.span_bug(span, "pointer in a path in generic `derive`") } + Tuple(..) => { cx.span_bug(span, "tuple in a path in generic `derive`") } + } + } +} + + +fn mk_ty_param(cx: &ExtCtxt, + span: Span, + name: &str, + bounds: &[Path], + self_ident: Ident, + self_generics: &Generics) + -> ast::TyParam { + let bounds = + bounds.iter().map(|b| { + let path = b.to_path(cx, span, self_ident, self_generics); + cx.typarambound(path) + }).collect(); + cx.typaram(span, cx.ident_of(name), bounds, None) +} + +fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>) + -> Generics { + Generics { + lifetimes: lifetimes, + ty_params: OwnedSlice::from_vec(ty_params), + where_clause: ast::WhereClause { + id: ast::DUMMY_NODE_ID, + predicates: Vec::new(), + }, + } +} + +/// Lifetimes and bounds on type parameters +#[derive(Clone)] +pub struct LifetimeBounds<'a> { + pub lifetimes: Vec<(&'a str, Vec<&'a str>)>, + pub bounds: Vec<(&'a str, Vec<Path<'a>>)>, +} + +impl<'a> LifetimeBounds<'a> { + pub fn empty() -> LifetimeBounds<'a> { + LifetimeBounds { + lifetimes: Vec::new(), bounds: Vec::new() + } + } + pub fn to_generics(&self, + cx: &ExtCtxt, + span: Span, + self_ty: Ident, + self_generics: &Generics) + -> Generics { + let lifetimes = self.lifetimes.iter().map(|&(ref lt, ref bounds)| { + let bounds = + bounds.iter().map( + |b| cx.lifetime(span, cx.ident_of(*b).name)).collect(); + cx.lifetime_def(span, cx.ident_of(*lt).name, bounds) + }).collect(); + let ty_params = self.bounds.iter().map(|t| { + match *t { + (ref name, ref bounds) => { + mk_ty_param(cx, + span, + *name, + bounds, + self_ty, + self_generics) + } + } + }).collect(); + mk_generics(lifetimes, ty_params) + } +} + +pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option<PtrTy>) + -> (P<Expr>, ast::ExplicitSelf) { + // this constructs a fresh `self` path, which will match the fresh `self` binding + // created below. + let self_path = cx.expr_self(span); + match *self_ptr { + None => { + (self_path, respan(span, ast::SelfValue(special_idents::self_))) + } + Some(ref ptr) => { + let self_ty = respan( + span, + match *ptr { + Borrowed(ref lt, mutbl) => { + let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name)); + ast::SelfRegion(lt, mutbl, special_idents::self_) + } + Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition") + }); + let self_expr = cx.expr_deref(span, self_path); + (self_expr, self_ty) + } + } +} diff --git a/src/libsyntax_ext/deriving/hash.rs b/src/libsyntax_ext/deriving/hash.rs new file mode 100644 index 00000000000..6bd21f7c0e0 --- /dev/null +++ b/src/libsyntax_ext/deriving/hash.rs @@ -0,0 +1,100 @@ +// 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. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr, MutMutable}; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::ptr::P; + +pub fn expand_deriving_hash(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + + let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None, + vec!(), true); + let arg = Path::new_local("__H"); + let hash_trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path, + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "hash", + generics: LifetimeBounds { + lifetimes: Vec::new(), + bounds: vec![("__H", + vec![path_std!(cx, core::hash::Hasher)])], + }, + explicit_self: borrowed_explicit_self(), + args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, MutMutable))), + ret_ty: nil_ty(), + attributes: vec![], + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + hash_substructure(a, b, c) + })) + } + ), + associated_types: Vec::new(), + }; + + hash_trait_def.expand(cx, mitem, item, push); +} + +fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { + let state_expr = match (substr.nonself_args.len(), substr.nonself_args.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`") + }; + let call_hash = |span, thing_expr| { + let hash_path = { + let strs = cx.std_path(&["hash", "Hash", "hash"]); + + cx.expr_path(cx.path_global(span, strs)) + }; + let ref_thing = cx.expr_addr_of(span, thing_expr); + let expr = cx.expr_call(span, hash_path, vec!(ref_thing, state_expr.clone())); + cx.stmt_expr(expr) + }; + let mut stmts = Vec::new(); + + let fields = match *substr.fields { + Struct(ref fs) => fs, + EnumMatching(index, variant, ref fs) => { + // Determine the discriminant. We will feed this value to the byte + // iteration function. + let discriminant = match variant.node.disr_expr { + Some(ref d) => d.clone(), + None => cx.expr_usize(trait_span, index) + }; + + stmts.push(call_hash(trait_span, discriminant)); + + fs + } + _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`") + }; + + for &FieldInfo { ref self_, span, .. } in fields { + stmts.push(call_hash(span, self_.clone())); + } + + cx.expr_block(cx.block(trait_span, stmts, None)) +} diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs new file mode 100644 index 00000000000..919540724ca --- /dev/null +++ b/src/libsyntax_ext/deriving/mod.rs @@ -0,0 +1,202 @@ +// Copyright 2012-2015 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. + +//! The compiler code necessary to implement the `#[derive]` extensions. +//! +//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is +//! the standard library, and "std" is the core library. + +use syntax::ast::{MetaItem, MetaWord}; +use syntax::attr::AttrMetaMethods; +use syntax::ext::base::{ExtCtxt, SyntaxEnv, Annotatable}; +use syntax::ext::base::{MultiDecorator, MultiItemDecorator, MultiModifier}; +use syntax::ext::build::AstBuilder; +use syntax::feature_gate; +use syntax::codemap::Span; +use syntax::parse::token::{intern, intern_and_get_ident}; + +macro_rules! pathvec { + ($($x:ident)::+) => ( + vec![ $( stringify!($x) ),+ ] + ) +} + +macro_rules! path { + ($($x:tt)*) => ( + ::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) ) + ) +} + +macro_rules! path_local { + ($x:ident) => ( + ::deriving::generic::ty::Path::new_local(stringify!($x)) + ) +} + +macro_rules! pathvec_std { + ($cx:expr, $first:ident :: $($rest:ident)::+) => ({ + let mut v = pathvec!($($rest)::+); + if let Some(s) = $cx.crate_root { + v.insert(0, s); + } + v + }) +} + +macro_rules! path_std { + ($($x:tt)*) => ( + ::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) ) + ) +} + +pub mod bounds; +pub mod clone; +pub mod encodable; +pub mod decodable; +pub mod hash; +pub mod debug; +pub mod default; +pub mod primitive; + +#[path="cmp/partial_eq.rs"] +pub mod partial_eq; +#[path="cmp/eq.rs"] +pub mod eq; +#[path="cmp/partial_ord.rs"] +pub mod partial_ord; +#[path="cmp/ord.rs"] +pub mod ord; + + +pub mod generic; + +fn expand_derive(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + annotatable: Annotatable) + -> Annotatable { + annotatable.map_item_or(|item| { + item.map(|mut item| { + if mitem.value_str().is_some() { + cx.span_err(mitem.span, "unexpected value in `derive`"); + } + + let traits = mitem.meta_item_list().unwrap_or(&[]); + if traits.is_empty() { + cx.span_warn(mitem.span, "empty trait list in `derive`"); + } + + for titem in traits.iter().rev() { + let tname = match titem.node { + MetaWord(ref tname) => tname, + _ => { + cx.span_err(titem.span, "malformed `derive` entry"); + continue; + } + }; + + if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) { + feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, + "custom_derive", + titem.span, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_CUSTOM_DERIVE); + continue; + } + + // #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar] + item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span, + intern_and_get_ident(&format!("derive_{}", tname))))); + } + + item + }) + }, |a| { + cx.span_err(span, "`derive` can only be applied to items"); + a + }) +} + +macro_rules! derive_traits { + ($( $name:expr => $func:path, )+) => { + pub fn register_all(env: &mut SyntaxEnv) { + // Define the #[derive_*] extensions. + $({ + struct DeriveExtension; + + impl MultiItemDecorator for DeriveExtension { + fn expand(&self, + ecx: &mut ExtCtxt, + sp: Span, + mitem: &MetaItem, + annotatable: &Annotatable, + push: &mut FnMut(Annotatable)) { + warn_if_deprecated(ecx, sp, $name); + $func(ecx, sp, mitem, annotatable, push); + } + } + + env.insert(intern(concat!("derive_", $name)), + MultiDecorator(Box::new(DeriveExtension))); + })+ + + env.insert(intern("derive"), + MultiModifier(Box::new(expand_derive))); + } + + fn is_builtin_trait(name: &str) -> bool { + match name { + $( $name )|+ => true, + _ => false, + } + } + } +} + +derive_traits! { + "Clone" => clone::expand_deriving_clone, + + "Hash" => hash::expand_deriving_hash, + + "RustcEncodable" => encodable::expand_deriving_rustc_encodable, + + "RustcDecodable" => decodable::expand_deriving_rustc_decodable, + + "PartialEq" => partial_eq::expand_deriving_partial_eq, + "Eq" => eq::expand_deriving_eq, + "PartialOrd" => partial_ord::expand_deriving_partial_ord, + "Ord" => ord::expand_deriving_ord, + + "Debug" => debug::expand_deriving_debug, + + "Default" => default::expand_deriving_default, + + "FromPrimitive" => primitive::expand_deriving_from_primitive, + + "Send" => bounds::expand_deriving_unsafe_bound, + "Sync" => bounds::expand_deriving_unsafe_bound, + "Copy" => bounds::expand_deriving_copy, + + // deprecated + "Encodable" => encodable::expand_deriving_encodable, + "Decodable" => decodable::expand_deriving_decodable, +} + +#[inline] // because `name` is a compile-time constant +fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) { + if let Some(replacement) = match name { + "Encodable" => Some("RustcEncodable"), + "Decodable" => Some("RustcDecodable"), + _ => None, + } { + ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})", + name, replacement)); + } +} diff --git a/src/libsyntax_ext/deriving/primitive.rs b/src/libsyntax_ext/deriving/primitive.rs new file mode 100644 index 00000000000..121fe01976e --- /dev/null +++ b/src/libsyntax_ext/deriving/primitive.rs @@ -0,0 +1,142 @@ +// 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use deriving::generic::*; +use deriving::generic::ty::*; + +use syntax::ast::{MetaItem, Expr}; +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::{ExtCtxt, Annotatable}; +use syntax::ext::build::AstBuilder; +use syntax::parse::token::InternedString; +use syntax::ptr::P; + +pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut FnMut(Annotatable)) +{ + let inline = cx.meta_word(span, InternedString::new("inline")); + let attrs = vec!(cx.attribute(span, inline)); + let trait_def = TraitDef { + span: span, + attributes: Vec::new(), + path: path_std!(cx, core::num::FromPrimitive), + additional_bounds: Vec::new(), + generics: LifetimeBounds::empty(), + is_unsafe: false, + methods: vec!( + MethodDef { + name: "from_i64", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: vec!(Literal(path_local!(i64))), + ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option), + None, + vec!(Box::new(Self_)), + true)), + // #[inline] liable to cause code-bloat + attributes: attrs.clone(), + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_from("i64", c, s, sub) + })), + }, + MethodDef { + name: "from_u64", + generics: LifetimeBounds::empty(), + explicit_self: None, + args: vec!(Literal(path_local!(u64))), + ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option), + None, + vec!(Box::new(Self_)), + true)), + // #[inline] liable to cause code-bloat + attributes: attrs, + is_unsafe: false, + combine_substructure: combine_substructure(Box::new(|c, s, sub| { + cs_from("u64", c, s, sub) + })), + } + ), + associated_types: Vec::new(), + }; + + trait_def.expand(cx, mitem, item, push) +} + +fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> { + let n = match (substr.nonself_args.len(), substr.nonself_args.get(0)) { + (1, Some(o_f)) => o_f, + _ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`") + }; + + match *substr.fields { + StaticStruct(..) => { + cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs"); + return cx.expr_fail(trait_span, InternedString::new("")); + } + StaticEnum(enum_def, _) => { + if enum_def.variants.is_empty() { + cx.span_err(trait_span, + "`FromPrimitive` cannot be derived for enums with no variants"); + return cx.expr_fail(trait_span, InternedString::new("")); + } + + let mut arms = Vec::new(); + + for variant in &enum_def.variants { + let def = &variant.node.data; + if !def.is_unit() { + cx.span_err(trait_span, "`FromPrimitive` cannot be derived \ + for enums with non-unit variants"); + return cx.expr_fail(trait_span, + InternedString::new("")); + } + + let span = variant.span; + + // expr for `$n == $variant as $name` + let path = cx.path(span, vec![substr.type_ident, variant.node.name]); + let variant = cx.expr_path(path); + let ty = cx.ty_ident(span, cx.ident_of(name)); + let cast = cx.expr_cast(span, variant.clone(), ty); + let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast); + + // expr for `Some($variant)` + let body = cx.expr_some(span, variant); + + // arm for `_ if $guard => $body` + let arm = ast::Arm { + attrs: vec!(), + pats: vec!(cx.pat_wild(span)), + guard: Some(guard), + body: body, + }; + + arms.push(arm); + } + + // arm for `_ => None` + let arm = ast::Arm { + attrs: vec!(), + pats: vec!(cx.pat_wild(trait_span)), + guard: None, + body: cx.expr_none(trait_span), + }; + arms.push(arm); + + cx.expr_match(trait_span, n.clone(), arms) + } + _ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)") + } +} diff --git a/src/libsyntax_ext/env.rs b/src/libsyntax_ext/env.rs new file mode 100644 index 00000000000..f1dd6854a3a --- /dev/null +++ b/src/libsyntax_ext/env.rs @@ -0,0 +1,106 @@ +// 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 <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. + +/* + * The compiler code necessary to support the env! extension. Eventually this + * should all get sucked into either the compiler syntax extension plugin + * interface. + */ + +use syntax::ast; +use syntax::codemap::Span; +use syntax::ext::base::*; +use syntax::ext::base; +use syntax::ext::build::AstBuilder; +use syntax::parse::token; + +use std::env; + +pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { + let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") { + None => return DummyResult::expr(sp), + Some(v) => v + }; + + let e = match env::var(&var[..]) { + Err(..) => { + cx.expr_path(cx.path_all(sp, + true, + cx.std_path(&["option", "Option", "None"]), + Vec::new(), + vec!(cx.ty_rptr(sp, + cx.ty_ident(sp, + cx.ident_of("str")), + Some(cx.lifetime(sp, + cx.ident_of( + "'static").name)), + ast::MutImmutable)), + Vec::new())) + } + Ok(s) => { + cx.expr_call_global(sp, + cx.std_path(&["option", "Option", "Some"]), + vec!(cx.expr_str(sp, + token::intern_and_get_ident( + &s[..])))) + } + }; + MacEager::expr(e) +} + +pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { + let mut exprs = match get_exprs_from_tts(cx, sp, tts) { + Some(ref exprs) if exprs.is_empty() => { + cx.span_err(sp, "env! takes 1 or 2 arguments"); + return DummyResult::expr(sp); + } + None => return DummyResult::expr(sp), + Some(exprs) => exprs.into_iter() + }; + + let var = match expr_to_string(cx, + exprs.next().unwrap(), + "expected string literal") { + None => return DummyResult::expr(sp), + Some((v, _style)) => v + }; + let msg = match exprs.next() { + None => { + token::intern_and_get_ident(&format!("environment variable `{}` \ + not defined", + var)) + } + Some(second) => { + match expr_to_string(cx, second, "expected string literal") { + None => return DummyResult::expr(sp), + Some((s, _style)) => s + } + } + }; + + match exprs.next() { + None => {} + Some(_) => { + cx.span_err(sp, "env! takes 1 or 2 arguments"); + return DummyResult::expr(sp); + } + } + + let e = match env::var(&var[..]) { + Err(_) => { + cx.span_err(sp, &msg); + cx.expr_usize(sp, 0) + } + Ok(s) => cx.expr_str(sp, token::intern_and_get_ident(&s)) + }; + MacEager::expr(e) +} diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs new file mode 100644 index 00000000000..24094f797e6 --- /dev/null +++ b/src/libsyntax_ext/format.rs @@ -0,0 +1,716 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::ArgumentType::*; +use self::Position::*; + +use fmt_macros as parse; + +use syntax::ast; +use syntax::codemap::{Span, respan}; +use syntax::ext::base::*; +use syntax::ext::base; +use syntax::ext::build::AstBuilder; +use syntax::fold::Folder; +use syntax::parse::token::special_idents; +use syntax::parse::token; +use syntax::ptr::P; + +use std::collections::HashMap; + +#[derive(PartialEq)] +enum ArgumentType { + Known(String), + Unsigned +} + +enum Position { + Exact(usize), + Named(String), +} + +struct Context<'a, 'b:'a> { + ecx: &'a mut ExtCtxt<'b>, + /// The macro's call site. References to unstable formatting internals must + /// use this span to pass the stability checker. + macsp: Span, + /// The span of the format string literal. + fmtsp: Span, + + /// Parsed argument expressions and the types that we've found so far for + /// them. + args: Vec<P<ast::Expr>>, + arg_types: Vec<Option<ArgumentType>>, + /// Parsed named expressions and the types that we've found for them so far. + /// Note that we keep a side-array of the ordering of the named arguments + /// found to be sure that we can translate them in the same order that they + /// were declared in. + names: HashMap<String, P<ast::Expr>>, + name_types: HashMap<String, ArgumentType>, + name_ordering: Vec<String>, + + /// The latest consecutive literal strings, or empty if there weren't any. + literal: String, + + /// Collection of the compiled `rt::Argument` structures + pieces: Vec<P<ast::Expr>>, + /// Collection of string literals + str_pieces: Vec<P<ast::Expr>>, + /// Stays `true` if all formatting parameters are default (as in "{}{}"). + all_pieces_simple: bool, + + name_positions: HashMap<String, usize>, + + /// Updated as arguments are consumed or methods are entered + nest_level: usize, + next_arg: usize, +} + +/// Parses the arguments from the given list of tokens, returning None +/// if there's a parse error so we can continue parsing other format! +/// expressions. +/// +/// If parsing succeeds, the return value is: +/// ```ignore +/// Some((fmtstr, unnamed arguments, ordering of named arguments, +/// named arguments)) +/// ``` +fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) + -> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>, + HashMap<String, P<ast::Expr>>)> { + let mut args = Vec::new(); + let mut names = HashMap::<String, P<ast::Expr>>::new(); + let mut order = Vec::new(); + + let mut p = ecx.new_parser_from_tts(tts); + + if p.token == token::Eof { + ecx.span_err(sp, "requires at least a format string argument"); + return None; + } + let fmtstr = panictry!(p.parse_expr()); + let mut named = false; + while p.token != token::Eof { + if !panictry!(p.eat(&token::Comma)) { + ecx.span_err(sp, "expected token: `,`"); + return None; + } + if p.token == token::Eof { break } // accept trailing commas + if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) { + named = true; + let ident = match p.token { + token::Ident(i, _) => { + panictry!(p.bump()); + i + } + _ if named => { + ecx.span_err(p.span, + "expected ident, positional arguments \ + cannot follow named arguments"); + return None; + } + _ => { + ecx.span_err(p.span, + &format!("expected ident for named argument, found `{}`", + p.this_token_to_string())); + return None; + } + }; + let name: &str = &ident.name.as_str(); + + panictry!(p.expect(&token::Eq)); + let e = panictry!(p.parse_expr()); + match names.get(name) { + None => {} + Some(prev) => { + ecx.span_err(e.span, + &format!("duplicate argument named `{}`", + name)); + ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here"); + continue + } + } + order.push(name.to_string()); + names.insert(name.to_string(), e); + } else { + args.push(panictry!(p.parse_expr())); + } + } + Some((fmtstr, args, order, names)) +} + +impl<'a, 'b> Context<'a, 'b> { + /// Verifies one piece of a parse string. All errors are not emitted as + /// fatal so we can continue giving errors about this and possibly other + /// format strings. + fn verify_piece(&mut self, p: &parse::Piece) { + match *p { + parse::String(..) => {} + parse::NextArgument(ref arg) => { + // width/precision first, if they have implicit positional + // parameters it makes more sense to consume them first. + self.verify_count(arg.format.width); + self.verify_count(arg.format.precision); + + // argument second, if it's an implicit positional parameter + // it's written second, so it should come after width/precision. + let pos = match arg.position { + parse::ArgumentNext => { + let i = self.next_arg; + if self.check_positional_ok() { + self.next_arg += 1; + } + Exact(i) + } + parse::ArgumentIs(i) => Exact(i), + parse::ArgumentNamed(s) => Named(s.to_string()), + }; + + let ty = Known(arg.format.ty.to_string()); + self.verify_arg_type(pos, ty); + } + } + } + + fn verify_count(&mut self, c: parse::Count) { + match c { + parse::CountImplied | parse::CountIs(..) => {} + parse::CountIsParam(i) => { + self.verify_arg_type(Exact(i), Unsigned); + } + parse::CountIsName(s) => { + self.verify_arg_type(Named(s.to_string()), Unsigned); + } + parse::CountIsNextParam => { + if self.check_positional_ok() { + let next_arg = self.next_arg; + self.verify_arg_type(Exact(next_arg), Unsigned); + self.next_arg += 1; + } + } + } + } + + fn check_positional_ok(&mut self) -> bool { + if self.nest_level != 0 { + self.ecx.span_err(self.fmtsp, "cannot use implicit positional \ + arguments nested inside methods"); + false + } else { + true + } + } + + fn describe_num_args(&self) -> String { + match self.args.len() { + 0 => "no arguments given".to_string(), + 1 => "there is 1 argument".to_string(), + x => format!("there are {} arguments", x), + } + } + + fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) { + match arg { + Exact(arg) => { + if self.args.len() <= arg { + let msg = format!("invalid reference to argument `{}` ({})", + arg, self.describe_num_args()); + + self.ecx.span_err(self.fmtsp, &msg[..]); + return; + } + { + let arg_type = match self.arg_types[arg] { + None => None, + Some(ref x) => Some(x) + }; + self.verify_same(self.args[arg].span, &ty, arg_type); + } + if self.arg_types[arg].is_none() { + self.arg_types[arg] = Some(ty); + } + } + + Named(name) => { + let span = match self.names.get(&name) { + Some(e) => e.span, + None => { + let msg = format!("there is no argument named `{}`", name); + self.ecx.span_err(self.fmtsp, &msg[..]); + return; + } + }; + self.verify_same(span, &ty, self.name_types.get(&name)); + if !self.name_types.contains_key(&name) { + self.name_types.insert(name.clone(), ty); + } + // Assign this named argument a slot in the arguments array if + // it hasn't already been assigned a slot. + if !self.name_positions.contains_key(&name) { + let slot = self.name_positions.len(); + self.name_positions.insert(name, slot); + } + } + } + } + + /// When we're keeping track of the types that are declared for certain + /// arguments, we assume that `None` means we haven't seen this argument + /// yet, `Some(None)` means that we've seen the argument, but no format was + /// specified, and `Some(Some(x))` means that the argument was declared to + /// have type `x`. + /// + /// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true + /// that: `Some(None) == Some(Some(x))` + fn verify_same(&self, + sp: Span, + ty: &ArgumentType, + before: Option<&ArgumentType>) { + let cur = match before { + None => return, + Some(t) => t, + }; + if *ty == *cur { + return + } + match (cur, ty) { + (&Known(ref cur), &Known(ref ty)) => { + self.ecx.span_err(sp, + &format!("argument redeclared with type `{}` when \ + it was previously `{}`", + *ty, + *cur)); + } + (&Known(ref cur), _) => { + self.ecx.span_err(sp, + &format!("argument used to format with `{}` was \ + attempted to not be used for formatting", + *cur)); + } + (_, &Known(ref ty)) => { + self.ecx.span_err(sp, + &format!("argument previously used as a format \ + argument attempted to be used as `{}`", + *ty)); + } + (_, _) => { + self.ecx.span_err(sp, "argument declared with multiple formats"); + } + } + } + + fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> { + ecx.std_path(&["fmt", "rt", "v1", s]) + } + + fn trans_count(&self, c: parse::Count) -> P<ast::Expr> { + let sp = self.macsp; + let count = |c, arg| { + let mut path = Context::rtpath(self.ecx, "Count"); + path.push(self.ecx.ident_of(c)); + match arg { + Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]), + None => self.ecx.expr_path(self.ecx.path_global(sp, path)), + } + }; + match c { + parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))), + parse::CountIsParam(i) => { + count("Param", Some(self.ecx.expr_usize(sp, i))) + } + parse::CountImplied => count("Implied", None), + parse::CountIsNextParam => count("NextParam", None), + parse::CountIsName(n) => { + let i = match self.name_positions.get(n) { + Some(&i) => i, + None => 0, // error already emitted elsewhere + }; + let i = i + self.args.len(); + count("Param", Some(self.ecx.expr_usize(sp, i))) + } + } + } + + /// Translate the accumulated string literals to a literal expression + fn trans_literal_string(&mut self) -> P<ast::Expr> { + let sp = self.fmtsp; + let s = token::intern_and_get_ident(&self.literal); + self.literal.clear(); + self.ecx.expr_str(sp, s) + } + + /// Translate a `parse::Piece` to a static `rt::Argument` or append + /// to the `literal` string. + fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> { + let sp = self.macsp; + match *piece { + parse::String(s) => { + self.literal.push_str(s); + None + } + parse::NextArgument(ref arg) => { + // Translate the position + let pos = { + let pos = |c, arg| { + let mut path = Context::rtpath(self.ecx, "Position"); + path.push(self.ecx.ident_of(c)); + match arg { + Some(i) => { + let arg = self.ecx.expr_usize(sp, i); + self.ecx.expr_call_global(sp, path, vec![arg]) + } + None => { + self.ecx.expr_path(self.ecx.path_global(sp, path)) + } + } + }; + match arg.position { + // These two have a direct mapping + parse::ArgumentNext => pos("Next", None), + parse::ArgumentIs(i) => pos("At", Some(i)), + + // Named arguments are converted to positional arguments + // at the end of the list of arguments + parse::ArgumentNamed(n) => { + let i = match self.name_positions.get(n) { + Some(&i) => i, + None => 0, // error already emitted elsewhere + }; + let i = i + self.args.len(); + pos("At", Some(i)) + } + } + }; + + let simple_arg = parse::Argument { + position: parse::ArgumentNext, + format: parse::FormatSpec { + fill: arg.format.fill, + align: parse::AlignUnknown, + flags: 0, + precision: parse::CountImplied, + width: parse::CountImplied, + ty: arg.format.ty + } + }; + + let fill = match arg.format.fill { Some(c) => c, None => ' ' }; + + if *arg != simple_arg || fill != ' ' { + self.all_pieces_simple = false; + } + + // Translate the format + let fill = self.ecx.expr_lit(sp, ast::LitChar(fill)); + let align = |name| { + let mut p = Context::rtpath(self.ecx, "Alignment"); + p.push(self.ecx.ident_of(name)); + self.ecx.path_global(sp, p) + }; + let align = match arg.format.align { + parse::AlignLeft => align("Left"), + parse::AlignRight => align("Right"), + parse::AlignCenter => align("Center"), + parse::AlignUnknown => align("Unknown"), + }; + let align = self.ecx.expr_path(align); + let flags = self.ecx.expr_u32(sp, arg.format.flags); + let prec = self.trans_count(arg.format.precision); + let width = self.trans_count(arg.format.width); + let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec")); + let fmt = self.ecx.expr_struct(sp, path, vec!( + self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill), + self.ecx.field_imm(sp, self.ecx.ident_of("align"), align), + self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags), + self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec), + self.ecx.field_imm(sp, self.ecx.ident_of("width"), width))); + + let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument")); + Some(self.ecx.expr_struct(sp, path, vec!( + self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos), + self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt)))) + } + } + } + + fn static_array(ecx: &mut ExtCtxt, + name: &str, + piece_ty: P<ast::Ty>, + pieces: Vec<P<ast::Expr>>) + -> P<ast::Expr> { + let sp = piece_ty.span; + let ty = ecx.ty_rptr(sp, + ecx.ty(sp, ast::TyVec(piece_ty)), + Some(ecx.lifetime(sp, special_idents::static_lifetime.name)), + ast::MutImmutable); + let slice = ecx.expr_vec_slice(sp, pieces); + // static instead of const to speed up codegen by not requiring this to be inlined + let st = ast::ItemStatic(ty, ast::MutImmutable, slice); + + let name = ecx.ident_of(name); + let item = ecx.item(sp, name, vec![], st); + let decl = respan(sp, ast::DeclItem(item)); + + // Wrap the declaration in a block so that it forms a single expression. + ecx.expr_block(ecx.block(sp, + vec![P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))], + Some(ecx.expr_ident(sp, name)))) + } + + /// Actually builds the expression which the iformat! block will be expanded + /// to + fn into_expr(mut self) -> P<ast::Expr> { + let mut locals = Vec::new(); + let mut names = vec![None; self.name_positions.len()]; + let mut pats = Vec::new(); + let mut heads = Vec::new(); + + // First, build up the static array which will become our precompiled + // format "string" + let static_lifetime = self.ecx.lifetime(self.fmtsp, special_idents::static_lifetime.name); + let piece_ty = self.ecx.ty_rptr( + self.fmtsp, + self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")), + Some(static_lifetime), + ast::MutImmutable); + let pieces = Context::static_array(self.ecx, + "__STATIC_FMTSTR", + piece_ty, + self.str_pieces); + + + // Right now there is a bug such that for the expression: + // foo(bar(&1)) + // the lifetime of `1` doesn't outlast the call to `bar`, so it's not + // valid for the call to `foo`. To work around this all arguments to the + // format! string are shoved into locals. Furthermore, we shove the address + // of each variable because we don't want to move out of the arguments + // passed to this function. + for (i, e) in self.args.into_iter().enumerate() { + let arg_ty = match self.arg_types[i].as_ref() { + Some(ty) => ty, + None => continue // error already generated + }; + + let name = self.ecx.ident_of(&format!("__arg{}", i)); + pats.push(self.ecx.pat_ident(e.span, name)); + locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, + self.ecx.expr_ident(e.span, name))); + heads.push(self.ecx.expr_addr_of(e.span, e)); + } + for name in &self.name_ordering { + let e = match self.names.remove(name) { + Some(e) => e, + None => continue + }; + let arg_ty = match self.name_types.get(name) { + Some(ty) => ty, + None => continue + }; + + let lname = self.ecx.ident_of(&format!("__arg{}", + *name)); + pats.push(self.ecx.pat_ident(e.span, lname)); + names[*self.name_positions.get(name).unwrap()] = + Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty, + self.ecx.expr_ident(e.span, lname))); + heads.push(self.ecx.expr_addr_of(e.span, e)); + } + + // Now create a vector containing all the arguments + let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap())); + + let args_array = self.ecx.expr_vec(self.fmtsp, args.collect()); + + // Constructs an AST equivalent to: + // + // match (&arg0, &arg1) { + // (tmp0, tmp1) => args_array + // } + // + // It was: + // + // let tmp0 = &arg0; + // let tmp1 = &arg1; + // args_array + // + // Because of #11585 the new temporary lifetime rule, the enclosing + // statements for these temporaries become the let's themselves. + // If one or more of them are RefCell's, RefCell borrow() will also + // end there; they don't last long enough for args_array to use them. + // The match expression solves the scope problem. + // + // Note, it may also very well be transformed to: + // + // match arg0 { + // ref tmp0 => { + // match arg1 => { + // ref tmp1 => args_array } } } + // + // But the nested match expression is proved to perform not as well + // as series of let's; the first approach does. + let pat = self.ecx.pat_tuple(self.fmtsp, pats); + let arm = self.ecx.arm(self.fmtsp, vec!(pat), args_array); + let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads)); + let result = self.ecx.expr_match(self.fmtsp, head, vec!(arm)); + + let args_slice = self.ecx.expr_addr_of(self.fmtsp, result); + + // Now create the fmt::Arguments struct with all our locals we created. + let (fn_name, fn_args) = if self.all_pieces_simple { + ("new_v1", vec![pieces, args_slice]) + } else { + // Build up the static array which will store our precompiled + // nonstandard placeholders, if there are any. + let piece_ty = self.ecx.ty_path(self.ecx.path_global( + self.macsp, + Context::rtpath(self.ecx, "Argument"))); + let fmt = Context::static_array(self.ecx, + "__STATIC_FMTARGS", + piece_ty, + self.pieces); + + ("new_v1_formatted", vec![pieces, args_slice, fmt]) + }; + + let path = self.ecx.std_path(&["fmt", "Arguments", fn_name]); + self.ecx.expr_call_global(self.macsp, path, fn_args) + } + + fn format_arg(ecx: &ExtCtxt, macsp: Span, sp: Span, + ty: &ArgumentType, arg: P<ast::Expr>) + -> P<ast::Expr> { + let trait_ = match *ty { + Known(ref tyname) => { + match &tyname[..] { + "" => "Display", + "?" => "Debug", + "e" => "LowerExp", + "E" => "UpperExp", + "o" => "Octal", + "p" => "Pointer", + "b" => "Binary", + "x" => "LowerHex", + "X" => "UpperHex", + _ => { + ecx.span_err(sp, + &format!("unknown format trait `{}`", + *tyname)); + "Dummy" + } + } + } + Unsigned => { + let path = ecx.std_path(&["fmt", "ArgumentV1", "from_usize"]); + return ecx.expr_call_global(macsp, path, vec![arg]) + } + }; + + let path = ecx.std_path(&["fmt", trait_, "fmt"]); + let format_fn = ecx.path_global(sp, path); + let path = ecx.std_path(&["fmt", "ArgumentV1", "new"]); + ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)]) + } +} + +pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { + + match parse_args(ecx, sp, tts) { + Some((efmt, args, order, names)) => { + MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, + args, order, names)) + } + None => DummyResult::expr(sp) + } +} + +/// Take the various parts of `format_args!(efmt, args..., name=names...)` +/// and construct the appropriate formatting expression. +pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span, + efmt: P<ast::Expr>, + args: Vec<P<ast::Expr>>, + name_ordering: Vec<String>, + names: HashMap<String, P<ast::Expr>>) + -> P<ast::Expr> { + let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect(); + let macsp = ecx.call_site(); + // Expand the format literal so that efmt.span will have a backtrace. This + // is essential for locating a bug when the format literal is generated in + // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")). + let efmt = ecx.expander().fold_expr(efmt); + let mut cx = Context { + ecx: ecx, + args: args, + arg_types: arg_types, + names: names, + name_positions: HashMap::new(), + name_types: HashMap::new(), + name_ordering: name_ordering, + nest_level: 0, + next_arg: 0, + literal: String::new(), + pieces: Vec::new(), + str_pieces: Vec::new(), + all_pieces_simple: true, + macsp: macsp, + fmtsp: efmt.span, + }; + let fmt = match expr_to_string(cx.ecx, + efmt, + "format argument must be a string literal.") { + Some((fmt, _)) => fmt, + None => return DummyResult::raw_expr(sp) + }; + + let mut parser = parse::Parser::new(&fmt); + + loop { + match parser.next() { + Some(piece) => { + if !parser.errors.is_empty() { break } + cx.verify_piece(&piece); + match cx.trans_piece(&piece) { + Some(piece) => { + let s = cx.trans_literal_string(); + cx.str_pieces.push(s); + cx.pieces.push(piece); + } + None => {} + } + } + None => break + } + } + if !parser.errors.is_empty() { + cx.ecx.span_err(cx.fmtsp, &format!("invalid format string: {}", + parser.errors.remove(0))); + return DummyResult::raw_expr(sp); + } + if !cx.literal.is_empty() { + let s = cx.trans_literal_string(); + cx.str_pieces.push(s); + } + + // Make sure that all arguments were used and all arguments have types. + for (i, ty) in cx.arg_types.iter().enumerate() { + if ty.is_none() { + cx.ecx.span_err(cx.args[i].span, "argument never used"); + } + } + for (name, e) in &cx.names { + if !cx.name_types.contains_key(name) { + cx.ecx.span_err(e.span, "named argument never used"); + } + } + + cx.into_expr() +} diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs new file mode 100644 index 00000000000..49c2618d51b --- /dev/null +++ b/src/libsyntax_ext/lib.rs @@ -0,0 +1,82 @@ +// Copyright 2015 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. + +//! Syntax extensions in the Rust compiler. + +#![crate_name = "syntax_ext"] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(rustc_private)] +#![feature(str_char)] + +extern crate fmt_macros; +extern crate syntax; + +use syntax::ext::base::{MacroExpanderFn, NormalTT}; +use syntax::ext::base::{SyntaxEnv, SyntaxExtension}; +use syntax::parse::token::intern; + +// A variant of 'try!' that panics on Err(FatalError). This is used as a +// crutch on the way towards a non-panic!-prone parser. It should be used +// for fatal parsing errors; eventually we plan to convert all code using +// panictry to just use normal try +macro_rules! panictry { + ($e:expr) => ({ + use std::result::Result::{Ok, Err}; + use syntax::diagnostic::FatalError; + match $e { + Ok(e) => e, + Err(FatalError) => panic!(FatalError) + } + }) +} + +mod asm; +mod cfg; +mod concat; +mod concat_idents; +mod deriving; +mod env; +mod format; +mod log_syntax; +mod trace_macros; + +pub fn register_builtins(env: &mut SyntaxEnv) { + // utility function to simplify creating NormalTT syntax extensions + fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { + NormalTT(Box::new(f), None, false) + } + + env.insert(intern("asm"), + builtin_normal_expander(asm::expand_asm)); + env.insert(intern("cfg"), + builtin_normal_expander(cfg::expand_cfg)); + env.insert(intern("concat"), + builtin_normal_expander(concat::expand_syntax_ext)); + env.insert(intern("concat_idents"), + builtin_normal_expander(concat_idents::expand_syntax_ext)); + env.insert(intern("env"), + builtin_normal_expander(env::expand_env)); + env.insert(intern("option_env"), + builtin_normal_expander(env::expand_option_env)); + env.insert(intern("format_args"), + // format_args uses `unstable` things internally. + NormalTT(Box::new(format::expand_format_args), None, true)); + env.insert(intern("log_syntax"), + builtin_normal_expander(log_syntax::expand_syntax_ext)); + env.insert(intern("trace_macros"), + builtin_normal_expander(trace_macros::expand_trace_macros)); + + deriving::register_all(env); +} diff --git a/src/libsyntax_ext/log_syntax.rs b/src/libsyntax_ext/log_syntax.rs new file mode 100644 index 00000000000..ee944abb645 --- /dev/null +++ b/src/libsyntax_ext/log_syntax.rs @@ -0,0 +1,34 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast; +use syntax::codemap; +use syntax::ext::base; +use syntax::feature_gate; +use syntax::print; + +pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt, + sp: codemap::Span, + tts: &[ast::TokenTree]) + -> Box<base::MacResult+'cx> { + if !cx.ecfg.enable_log_syntax() { + feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, + "log_syntax", + sp, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_LOG_SYNTAX); + return base::DummyResult::any(sp); + } + + println!("{}", print::pprust::tts_to_string(tts)); + + // any so that `log_syntax` can be invoked as an expression and item. + base::DummyResult::any(sp) +} diff --git a/src/libsyntax_ext/trace_macros.rs b/src/libsyntax_ext/trace_macros.rs new file mode 100644 index 00000000000..7b1e985442a --- /dev/null +++ b/src/libsyntax_ext/trace_macros.rs @@ -0,0 +1,43 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax::ast::TokenTree; +use syntax::codemap::Span; +use syntax::ext::base::ExtCtxt; +use syntax::ext::base; +use syntax::feature_gate; +use syntax::parse::token::keywords; + + +pub fn expand_trace_macros(cx: &mut ExtCtxt, + sp: Span, + tt: &[TokenTree]) + -> Box<base::MacResult+'static> { + if !cx.ecfg.enable_trace_macros() { + feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic, + "trace_macros", + sp, + feature_gate::GateIssue::Language, + feature_gate::EXPLAIN_TRACE_MACROS); + return base::DummyResult::any(sp); + } + + match (tt.len(), tt.first()) { + (1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::True) => { + cx.set_trace_macros(true); + } + (1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::False) => { + cx.set_trace_macros(false); + } + _ => cx.span_err(sp, "trace_macros! accepts only `true` or `false`"), + } + + base::DummyResult::any(sp) +} |
