Skip to content

Commit 6a22e11

Browse files
committed
[derive] List ignored parameters directly on the type
This removes our ugly dependency on nightly. Likewise, we can ignore lifetimes with #[zerogc(ignore_lifetimes)] Should fix #17
1 parent 83808f4 commit 6a22e11

File tree

3 files changed

+123
-122
lines changed

3 files changed

+123
-122
lines changed

libs/derive/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ proc-macro = true
1515
zerogc = { version = "0.1.3", path = "../.." }
1616

1717
[dependencies]
18-
indexmap = "1"
1918
# Proc macros
2019
syn = "1.0.55"
2120
quote = "1.0.8"

libs/derive/src/lib.rs

Lines changed: 119 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,73 +2,19 @@ extern crate proc_macro;
22

33
use quote::{quote, quote_spanned};
44
use syn::{
5-
parse_macro_input, parenthesized, parse_quote, DeriveInput, Data,
6-
Error, Generics, GenericParam, TypeParamBound, Fields, Member,
7-
Index, Type, GenericArgument, Attribute, PathArguments, Meta,
8-
TypeParam, WherePredicate, PredicateType, Token, Lifetime
5+
parse_macro_input, parenthesized, parse_quote, DeriveInput,
6+
Data, Error, Generics, GenericParam, TypeParamBound, Fields,
7+
Member, Index, Type, GenericArgument, Attribute, PathArguments,
8+
Meta, TypeParam, WherePredicate, PredicateType, Token, Lifetime,
9+
NestedMeta, Lit
910
};
1011
use proc_macro2::{Ident, TokenStream, Span};
1112
use syn::spanned::Spanned;
12-
use syn::parse::{ParseStream, Parse, ParseBuffer};
13+
use syn::parse::{ParseStream, Parse};
1314
use std::collections::HashSet;
1415
use syn::export::fmt::Display;
1516
use std::io::Write;
1617

17-
use indexmap::IndexMap;
18-
19-
struct AttributeOptions {
20-
ignored: bool
21-
}
22-
impl Default for AttributeOptions {
23-
fn default() -> AttributeOptions {
24-
AttributeOptions {
25-
ignored: false
26-
}
27-
}
28-
}
29-
impl AttributeOptions {
30-
pub fn find(attrs: &[Attribute]) -> Result<Self, Error> {
31-
attrs.iter().find_map(|attr| {
32-
if attr.path.is_ident("zerogc") {
33-
Some(syn::parse2::<AttributeOptions>(attr.tokens.clone()))
34-
} else {
35-
None
36-
}
37-
}).unwrap_or_else(|| Ok(AttributeOptions::default()))
38-
}
39-
}
40-
impl Parse for AttributeOptions {
41-
fn parse(raw_input: &ParseBuffer) -> Result<Self, Error> {
42-
let input;
43-
parenthesized!(input in raw_input);
44-
let mut result = AttributeOptions::default();
45-
while !input.is_empty() {
46-
let meta = input.parse::<Meta>()?;
47-
if meta.path().is_ident("ignore") {
48-
if !matches!(meta, Meta::Path(_)) {
49-
return Err(Error::new(
50-
meta.span(),
51-
"Malformed attribute for #[zerogc(ignore)]"
52-
))
53-
}
54-
if result.ignored {
55-
return Err(Error::new(
56-
meta.span(),
57-
"Already ignoring attribute"
58-
))
59-
}
60-
result.ignored = true;
61-
} else {
62-
return Err(Error::new(
63-
meta.span(),
64-
"Unknown attribute flag"
65-
));
66-
}
67-
}
68-
Ok(result)
69-
}
70-
}
71-
7218
struct MutableFieldOpts {
7319
public: bool
7420
}
@@ -155,50 +101,28 @@ impl Parse for GcFieldAttrs {
155101
}
156102

157103
struct GcTypeInfo {
158-
config: TypeConfig,
159-
params: IndexMap<Ident, AttributeOptions>,
160-
lifetimes: IndexMap<Lifetime, AttributeOptions>,
104+
config: TypeAttrs,
161105
}
162106
impl GcTypeInfo {
163-
fn ignored_params(&self) -> HashSet<Ident> {
164-
self.params.iter()
165-
.filter(|&(_, ref opts)| opts.ignored)
166-
.map(|(name, _)| name.clone())
167-
.collect()
168-
}
169107
fn parse(input: &DeriveInput) -> Result<GcTypeInfo, Error> {
170-
let config = TypeConfig::find(&*input.attrs)?;
171-
let mut params = IndexMap::new();
172-
let mut lifetimes = IndexMap::new();
173-
for lt in input.generics.lifetimes() {
174-
lifetimes.insert(
175-
lt.lifetime.clone(),
176-
AttributeOptions::find(&lt.attrs)?
177-
);
178-
}
179-
for param in input.generics.type_params() {
180-
params.insert(
181-
param.ident.clone(),
182-
AttributeOptions::find(&param.attrs)?
183-
);
184-
}
185-
if let Some(attrs) = lifetimes.get(&config.gc_lifetime()) {
186-
if attrs.ignored {
187-
return Err(Error::new(
188-
config.gc_lifetime().span(),
189-
"Ignored gc lifetime"
190-
))
191-
}
108+
let config = TypeAttrs::find(&*input.attrs)?;
109+
if config.ignored_lifetimes.contains(&config.gc_lifetime()) {
110+
return Err(Error::new(
111+
config.gc_lifetime().span(),
112+
"Ignored gc lifetime"
113+
))
192114
}
193-
Ok(GcTypeInfo { config, params, lifetimes })
115+
Ok(GcTypeInfo { config })
194116
}
195117
}
196-
struct TypeConfig {
118+
struct TypeAttrs {
197119
is_copy: bool,
198120
nop_trace: bool,
199-
gc_lifetime: Option<Lifetime>
121+
gc_lifetime: Option<Lifetime>,
122+
ignore_params: HashSet<Ident>,
123+
ignored_lifetimes: HashSet<Lifetime>
200124
}
201-
impl TypeConfig {
125+
impl TypeAttrs {
202126
fn gc_lifetime(&self) -> Lifetime {
203127
match self.gc_lifetime {
204128
Some(ref lt) => lt.clone(),
@@ -208,27 +132,29 @@ impl TypeConfig {
208132
pub fn find(attrs: &[Attribute]) -> Result<Self, Error> {
209133
attrs.iter().find_map(|attr| {
210134
if attr.path.is_ident("zerogc") {
211-
Some(syn::parse2::<TypeConfig>(attr.tokens.clone()))
135+
Some(syn::parse2::<TypeAttrs>(attr.tokens.clone()))
212136
} else {
213137
None
214138
}
215-
}).unwrap_or_else(|| Ok(TypeConfig::default()))
139+
}).unwrap_or_else(|| Ok(TypeAttrs::default()))
216140
}
217141
}
218-
impl Default for TypeConfig {
142+
impl Default for TypeAttrs {
219143
fn default() -> Self {
220-
TypeConfig {
144+
TypeAttrs {
221145
is_copy: false,
222146
nop_trace: false,
223-
gc_lifetime: None
147+
gc_lifetime: None,
148+
ignore_params: Default::default(),
149+
ignored_lifetimes: Default::default(),
224150
}
225151
}
226152
}
227-
impl Parse for TypeConfig {
153+
impl Parse for TypeAttrs {
228154
fn parse(raw_input: ParseStream) -> Result<Self, Error> {
229155
let input;
230156
parenthesized!(input in raw_input);
231-
let mut result = TypeConfig::default();
157+
let mut result = TypeAttrs::default();
232158
while !input.is_empty() {
233159
let meta = input.parse::<Meta>()?;
234160
if meta.path().is_ident("copy") {
@@ -268,7 +194,7 @@ impl Parse for TypeConfig {
268194
}
269195
let s = match meta {
270196
Meta::NameValue(syn::MetaNameValue {
271-
lit: ::syn::Lit::Str(ref s), ..
197+
lit: Lit::Str(ref s), ..
272198
}) => s,
273199
_ => {
274200
return Err(Error::new(
@@ -287,6 +213,89 @@ impl Parse for TypeConfig {
287213
}
288214
};
289215
result.gc_lifetime = Some(lifetime);
216+
} else if meta.path().is_ident("ignore_params") {
217+
if !result.ignore_params.is_empty() {
218+
return Err(Error::new(
219+
meta.span(),
220+
"Duplicate flags: #[zerogc(ignore_params)]"
221+
))
222+
}
223+
let list = match meta {
224+
Meta::List(ref list) if list.nested.is_empty() => {
225+
return Err(Error::new(
226+
list.span(),
227+
"Empty list for #[zerogc(ignore_params)]"
228+
))
229+
}
230+
Meta::List(list) => list,
231+
_ => return Err(Error::new(
232+
meta.span(),
233+
"Expected a list attribute for #[zerogc(ignore_params)]"
234+
))
235+
};
236+
for nested in list.nested {
237+
match nested {
238+
NestedMeta::Meta(Meta::Path(ref p))
239+
if p.get_ident().is_some() => {
240+
let ident = p.get_ident().unwrap();
241+
if !result.ignore_params.insert(ident.clone()) {
242+
return Err(Error::new(
243+
ident.span(),
244+
"Duplicate parameter to ignore"
245+
));
246+
}
247+
}
248+
_ => return Err(Error::new(
249+
nested.span(),
250+
"Invalid list value for #[zerogc(ignore_param)]"
251+
))
252+
}
253+
}
254+
} else if meta.path().is_ident("ignore_lifetimes") {
255+
if !result.ignore_params.is_empty() {
256+
return Err(Error::new(
257+
meta.span(),
258+
"Duplicate flags: #[zerogc(ignore_lifetimes)]"
259+
))
260+
}
261+
let list = match meta {
262+
Meta::List(ref list) if list.nested.is_empty() => {
263+
return Err(Error::new(
264+
list.span(),
265+
"Empty list for #[zerogc(ignore_lifetimes)]"
266+
))
267+
}
268+
Meta::List(list) => list,
269+
_ => return Err(Error::new(
270+
meta.span(),
271+
"Expected a list attribute for #[zerogc(ignore_lifetimes)]"
272+
))
273+
};
274+
for nested in list.nested {
275+
let lifetime = match nested {
276+
NestedMeta::Lit(Lit::Str(ref s)) => {
277+
s.parse::<Lifetime>()?
278+
},
279+
NestedMeta::Meta(Meta::Path(ref p)) if p.get_ident().is_some() => {
280+
let ident = p.get_ident().unwrap();
281+
Lifetime {
282+
ident: ident.clone(),
283+
// Fake the appostrophie's span as matching the ident
284+
apostrophe: ident.span()
285+
}
286+
},
287+
_ => return Err(Error::new(
288+
nested.span(),
289+
"Invalid list value for #[zerogc(ignore_lifetimes)]"
290+
))
291+
};
292+
if !result.ignored_lifetimes.insert(lifetime.clone()) {
293+
return Err(Error::new(
294+
lifetime.span(),
295+
"Duplicate lifetime to ignore"
296+
));
297+
}
298+
}
290299
} else {
291300
return Err(Error::new(
292301
meta.span(), "Unknown type flag"
@@ -300,7 +309,7 @@ impl Parse for TypeConfig {
300309
}
301310
}
302311

303-
#[proc_macro_derive(Trace)]
312+
#[proc_macro_derive(Trace, attributes(zerogc))]
304313
pub fn derive_trace(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
305314
let input = parse_macro_input!(input as DeriveInput);
306315
let res = From::from(impl_derive_trace(&input)
@@ -479,8 +488,8 @@ fn impl_brand(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
479488
GenericParam::Lifetime(ref l) => {
480489
if l.lifetime == info.config.gc_lifetime() {
481490
rewritten_param = parse_quote!('new_gc);
482-
assert!(!info.lifetimes[&l.lifetime].ignored);
483-
} else if info.lifetimes[&l.lifetime].ignored {
491+
assert!(!info.config.ignored_lifetimes.contains(&l.lifetime));
492+
} else if info.config.ignored_lifetimes.contains(&l.lifetime) {
484493
rewritten_param = GenericArgument::Lifetime(l.lifetime.clone());
485494
} else {
486495
return Err(Error::new(
@@ -509,11 +518,11 @@ fn impl_brand(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Er
509518
}
510519
})
511520
}
512-
fn impl_trace(target: &DeriveInput, attrs: &GcTypeInfo) -> Result<TokenStream, Error> {
521+
fn impl_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream, Error> {
513522
let name = &target.ident;
514523
let generics = add_trait_bounds_except(
515524
&target.generics, parse_quote!(zerogc::Trace),
516-
&attrs.ignored_params()
525+
&info.config.ignore_params
517526
)?;
518527
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
519528
let field_types: Vec<&Type>;
@@ -597,7 +606,7 @@ fn impl_gc_safe(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream,
597606
let name = &target.ident;
598607
let generics = add_trait_bounds_except(
599608
&target.generics, parse_quote!(zerogc::GcSafe),
600-
&info.ignored_params()
609+
&info.config.ignore_params
601610
)?;
602611
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
603612
let field_types: Vec<&Type> = match target.data {
@@ -667,7 +676,7 @@ fn impl_nop_trace(target: &DeriveInput, info: &GcTypeInfo) -> Result<TokenStream
667676
let name = &target.ident;
668677
let generics = add_trait_bounds_except(
669678
&target.generics, parse_quote!(zerogc::Trace),
670-
&info.ignored_params()
679+
&info.config.ignore_params
671680
)?;
672681
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
673682
let field_types: Vec<&Type>;

libs/derive/tests/basic.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
1-
/*
2-
* This is needed because we can't put attributes on lifetimes
3-
* and generic parameters right now.
4-
* TODO: Remove
5-
*/
6-
#![feature(register_attr)]
7-
#![register_attr(zerogc)]
8-
91
use zerogc::{Gc, CollectorId, Trace, GcSafe, NullTrace};
102

113
use zerogc_derive::Trace;
124

135
#[derive(Trace)]
14-
pub struct Basic<'gc, #[zerogc(ignore)] Id: CollectorId> {
6+
#[zerogc(ignore_params(Id))]
7+
pub struct Basic<'gc, Id: CollectorId> {
158
parent: Option<Gc<'gc, Basic<'gc, Id>, Id>>,
169
children: Vec<Gc<'gc, Basic<'gc, Id>, Id>>,
1710
value: String
1811
}
1912

2013
#[derive(Copy, Clone, Trace)]
21-
#[zerogc(copy)]
22-
pub struct BasicCopy<'gc, #[zerogc(ignore)] Id: CollectorId> {
14+
#[zerogc(copy, ignore_params(Id))]
15+
pub struct BasicCopy<'gc, Id: CollectorId> {
2316
test: i32,
2417
value: i32,
2518
basic: Option<Gc<'gc, Basic<'gc, Id>, Id>>

0 commit comments

Comments
 (0)