Skip to content

Commit decfedb

Browse files
committed
Fix num-macros FromPrimitive implementation
Current solution follow syntax proposed in rust-lang/rfcs#1681. Tracking issue rust-lang/rust#35900 Close #227
1 parent c8ed8ff commit decfedb

File tree

3 files changed

+68
-198
lines changed

3 files changed

+68
-198
lines changed

macros/Cargo.toml

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
[package]
2-
name = "num-macros"
3-
version = "0.1.33"
42
authors = ["The Rust Project Developers"]
5-
license = "MIT/Apache-2.0"
6-
homepage = "https://github.com/rust-num/num"
7-
repository = "https://github.com/rust-num/num"
3+
description = "Numeric syntax extensions"
84
documentation = "http://rust-num.github.io/num"
5+
homepage = "https://github.com/rust-num/num"
96
keywords = ["mathematics", "numerics"]
10-
description = "Numeric syntax extensions"
7+
license = "MIT/Apache-2.0"
8+
name = "num-macros"
9+
repository = "https://github.com/rust-num/num"
10+
version = "0.1.33"
1111

12-
[lib]
13-
name = "num_macros"
14-
plugin = true
12+
[dependencies]
13+
quote = "0.1.3"
14+
syn = "0.5.2"
1515

1616
[dev-dependencies]
17-
num = { path = "..", version = "0.1" }
17+
18+
[dev-dependencies.num]
19+
path = ".."
20+
version = "0.1"
21+
22+
[lib]
23+
crate-type = ["rustc-macro"]
24+
name = "num_macros"
25+
rustc-macro = true
26+
test = false

macros/src/lib.rs

+39-175
Original file line numberDiff line numberDiff line change
@@ -8,193 +8,57 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(plugin_registrar, rustc_private)]
11+
#![crate_type = "rustc-macro"]
12+
#![feature(rustc_macro, rustc_macro_lib)]
1213

13-
extern crate syntax;
14-
extern crate syntax_ext;
15-
extern crate rustc_plugin;
14+
extern crate syn;
15+
#[macro_use]
16+
extern crate quote;
17+
extern crate rustc_macro;
1618

17-
use syntax::ast::{MetaItem, Expr, BinOpKind};
18-
use syntax::ast;
19-
use syntax::codemap::Span;
20-
use syntax::ext::base::{ExtCtxt, Annotatable};
21-
use syntax::ext::build::AstBuilder;
22-
use syntax_ext::deriving::generic::*;
23-
use syntax_ext::deriving::generic::ty::*;
24-
use syntax::parse::token::InternedString;
25-
use syntax::ptr::P;
26-
use syntax::ext::base::MultiDecorator;
27-
use syntax::parse::token;
19+
use rustc_macro::TokenStream;
2820

29-
use rustc_plugin::Registry;
21+
use syn::Body::Enum;
3022

31-
macro_rules! pathvec {
32-
($($x:ident)::+) => (
33-
vec![ $( stringify!($x) ),+ ]
34-
)
35-
}
36-
37-
macro_rules! path {
38-
($($x:tt)*) => (
39-
::syntax_ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
40-
)
41-
}
23+
#[rustc_macro_derive(FromPrimitive)]
24+
pub fn from_primitive(input: TokenStream) -> TokenStream {
25+
let source = input.to_string();
4226

43-
macro_rules! path_local {
44-
($x:ident) => (
45-
::syntax_ext::deriving::generic::ty::Path::new_local(stringify!($x))
46-
)
47-
}
27+
let ast = syn::parse_item(&source).unwrap();
28+
// panic!("{:?}", ast);
4829

49-
macro_rules! pathvec_std {
50-
($cx:expr, $first:ident :: $($rest:ident)::+) => ({
51-
let mut v = pathvec!($($rest)::+);
52-
if let Some(s) = $cx.crate_root {
53-
v.insert(0, s);
54-
}
55-
v
56-
})
57-
}
58-
59-
pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
60-
span: Span,
61-
mitem: &MetaItem,
62-
item: &Annotatable,
63-
push: &mut FnMut(Annotatable))
64-
{
65-
let inline = cx.meta_word(span, InternedString::new("inline"));
66-
let attrs = vec!(cx.attribute(span, inline));
67-
let trait_def = TraitDef {
68-
is_unsafe: false,
69-
span: span,
70-
attributes: Vec::new(),
71-
path: path!(num::FromPrimitive),
72-
additional_bounds: Vec::new(),
73-
generics: LifetimeBounds::empty(),
74-
methods: vec!(
75-
MethodDef {
76-
name: "from_i64",
77-
is_unsafe: false,
78-
unify_fieldless_variants: false,
79-
generics: LifetimeBounds::empty(),
80-
explicit_self: None,
81-
args: vec!(Literal(path_local!(i64))),
82-
ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
83-
None,
84-
vec!(Box::new(Self_)),
85-
true)),
86-
// #[inline] liable to cause code-bloat
87-
attributes: attrs.clone(),
88-
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
89-
cs_from("i64", c, s, sub)
90-
})),
91-
},
92-
MethodDef {
93-
name: "from_u64",
94-
is_unsafe: false,
95-
unify_fieldless_variants: false,
96-
generics: LifetimeBounds::empty(),
97-
explicit_self: None,
98-
args: vec!(Literal(path_local!(u64))),
99-
ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
100-
None,
101-
vec!(Box::new(Self_)),
102-
true)),
103-
// #[inline] liable to cause code-bloat
104-
attributes: attrs,
105-
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
106-
cs_from("u64", c, s, sub)
107-
})),
108-
}
109-
),
110-
associated_types: Vec::new(),
30+
let name = &ast.ident;
31+
let variants: &[_] = match ast.body {
32+
Enum(ref variants) => variants,
33+
_ => panic!("#[derive(FromPrimitive)] works only with enums, struct given!"),
11134
};
11235

113-
trait_def.expand(cx, mitem, &item, push)
114-
}
115-
116-
fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
117-
if substr.nonself_args.len() != 1 {
118-
cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
119-
}
120-
121-
let n = &substr.nonself_args[0];
122-
123-
match *substr.fields {
124-
StaticStruct(..) => {
125-
cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
126-
return cx.expr_fail(trait_span, InternedString::new(""));
127-
}
128-
StaticEnum(enum_def, _) => {
129-
if enum_def.variants.is_empty() {
130-
cx.span_err(trait_span,
131-
"`FromPrimitive` cannot be derived for enums with no variants");
132-
return cx.expr_fail(trait_span, InternedString::new(""));
36+
let mut idx = 0;
37+
let variants: Vec<_> = variants.iter()
38+
.map(|variant| {
39+
let ident = &variant.ident;
40+
let tt = quote!(#idx => Some(#name::#ident));
41+
idx += 1;
42+
tt
43+
})
44+
.collect();
45+
46+
let res = quote! {
47+
#ast
48+
49+
impl ::num::traits::FromPrimitive for #name {
50+
fn from_i64(n: i64) -> Option<Self> {
51+
Self::from_u64(n as u64)
13352
}
13453

135-
let mut arms = Vec::new();
136-
137-
for variant in &enum_def.variants {
138-
match variant.node.data {
139-
ast::VariantData::Unit(..) => {
140-
let span = variant.span;
141-
142-
// expr for `$n == $variant as $name`
143-
let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
144-
let variant = cx.expr_path(path);
145-
let ty = cx.ty_ident(span, cx.ident_of(name));
146-
let cast = cx.expr_cast(span, variant.clone(), ty);
147-
let guard = cx.expr_binary(span, BinOpKind::Eq, n.clone(), cast);
148-
149-
// expr for `Some($variant)`
150-
let body = cx.expr_some(span, variant);
151-
152-
// arm for `_ if $guard => $body`
153-
let arm = ast::Arm {
154-
attrs: vec!(),
155-
pats: vec!(cx.pat_wild(span)),
156-
guard: Some(guard),
157-
body: body,
158-
};
159-
160-
arms.push(arm);
161-
}
162-
ast::VariantData::Tuple(..) => {
163-
cx.span_err(trait_span,
164-
"`FromPrimitive` cannot be derived for \
165-
enum variants with arguments");
166-
return cx.expr_fail(trait_span,
167-
InternedString::new(""));
168-
}
169-
ast::VariantData::Struct(..) => {
170-
cx.span_err(trait_span,
171-
"`FromPrimitive` cannot be derived for enums \
172-
with struct variants");
173-
return cx.expr_fail(trait_span,
174-
InternedString::new(""));
175-
}
54+
fn from_u64(n: u64) -> Option<Self> {
55+
match n {
56+
#(variants,)*
57+
_ => None,
17658
}
17759
}
178-
179-
// arm for `_ => None`
180-
let arm = ast::Arm {
181-
attrs: vec!(),
182-
pats: vec!(cx.pat_wild(trait_span)),
183-
guard: None,
184-
body: cx.expr_none(trait_span),
185-
};
186-
arms.push(arm);
187-
188-
cx.expr_match(trait_span, n.clone(), arms)
18960
}
190-
_ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
191-
}
192-
}
61+
};
19362

194-
#[plugin_registrar]
195-
#[doc(hidden)]
196-
pub fn plugin_registrar(reg: &mut Registry) {
197-
reg.register_syntax_extension(
198-
token::intern("derive_NumFromPrimitive"),
199-
MultiDecorator(Box::new(expand_deriving_from_primitive)));
63+
res.to_string().parse().unwrap()
20064
}

macros/tests/test_macro.rs

+10-13
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
#![feature(custom_derive, plugin)]
12-
#![plugin(num_macros)]
11+
#![feature(rustc_macro)]
1312

1413
extern crate num;
14+
#[macro_use]
15+
extern crate num_macros;
1516

16-
#[derive(Debug, PartialEq, NumFromPrimitive)]
17+
#[derive(Debug, PartialEq, FromPrimitive)]
1718
enum Color {
1819
Red,
1920
Blue,
@@ -22,15 +23,11 @@ enum Color {
2223

2324
#[test]
2425
fn test_from_primitive() {
25-
let v: Vec<Option<Color>> = vec![
26-
num::FromPrimitive::from_u64(0),
27-
num::FromPrimitive::from_u64(1),
28-
num::FromPrimitive::from_u64(2),
29-
num::FromPrimitive::from_u64(3),
30-
];
26+
let v: [Option<Color>; 4] = [num::FromPrimitive::from_u64(0),
27+
num::FromPrimitive::from_u64(1),
28+
num::FromPrimitive::from_u64(2),
29+
num::FromPrimitive::from_u64(3)];
3130

32-
assert_eq!(
33-
v,
34-
vec![Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]
35-
);
31+
assert_eq!(v,
32+
[Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]);
3633
}

0 commit comments

Comments
 (0)