Skip to content

Commit

Permalink
feat: Add derive for Traverseable
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Jun 25, 2019
1 parent d671dac commit 844418d
Show file tree
Hide file tree
Showing 19 changed files with 300 additions and 96 deletions.
7 changes: 7 additions & 0 deletions codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ mod functor;
mod getable;
mod pushable;
mod shared;
mod traverseable;
mod userdata;
mod vm_type;

Expand Down Expand Up @@ -190,6 +191,12 @@ pub fn vm_type(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
vm_type::derive(input.into()).into()
}

#[doc(hidden)]
#[proc_macro_derive(Traverseable, attributes(gluon))]
pub fn traverseable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
traverseable::derive(input.into()).into()
}

#[doc(hidden)]
#[proc_macro_derive(Functor, attributes(functor))]
pub fn functor(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Expand Down
200 changes: 200 additions & 0 deletions codegen/src/traverseable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
use std::borrow::Cow;

use {
proc_macro2::{Span, TokenStream},
shared::{map_type_params, split_for_impl},
syn::{
self, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, FieldsNamed, FieldsUnnamed,
Generics, Ident, Type, Variant,
},
};

use attr;

pub fn derive(input: TokenStream) -> TokenStream {
let derive_input = syn::parse2(input).expect("Input is checked by rustc");

let container = attr::Container::from_ast(&derive_input);

let DeriveInput {
ident,
data,
generics,
..
} = derive_input;

let tokens = match data {
Data::Struct(ast) => derive_struct(&container, ast, ident, generics),
Data::Enum(ast) => derive_enum(&container, ast, ident, generics),
Data::Union(_) => panic!("Unions are not supported"),
};

tokens.into()
}

fn derive_struct(
container: &attr::Container,
ast: DataStruct,
ident: Ident,
generics: Generics,
) -> TokenStream {
let cons = match ast.fields {
Fields::Named(FieldsNamed { named, .. }) => gen_struct_cons(&ident, &named),
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => gen_tuple_struct_cons(&ident, &unnamed),
Fields::Unit => quote! { #ident },
};

gen_impl(container, ident, generics, cons)
}

fn gen_struct_cons<'a, I>(_ident: &Ident, fields: I) -> TokenStream
where
I: IntoIterator<Item = &'a Field>,
{
let fields = fields.into_iter().map(|field| &field.ident);
quote! {
#(mark(&self.#fields, gc);)*
}
}

fn gen_tuple_struct_cons<'a, I>(ident: &Ident, fields: I) -> TokenStream
where
I: IntoIterator<Item = &'a Field>,
{
let fields = fields
.into_iter()
.enumerate()
.map(|(idx, _)| Ident::new(&format!("_{}", idx), Span::call_site()))
.collect::<Vec<_>>();
let fields = &fields;
quote! {
match self {
#ident(#(#fields,)*) => {
#(mark(#fields, gc);)*
}
}
}
}

fn derive_enum(
container: &attr::Container,
ast: DataEnum,
ident: Ident,
generics: Generics,
) -> TokenStream {
let cons;
{
let variants = ast
.variants
.iter()
.map(|variant| gen_variant_match(&ident, variant));

cons = quote! {

match self {
#(#variants,)*
}
};
}

gen_impl(container, ident, generics, cons)
}

fn gen_impl(
container: &attr::Container,
ident: Ident,
generics: Generics,
push_impl: TokenStream,
) -> TokenStream {
// generate bounds like T: Getable for every type parameter
let traverseable_bounds = create_traverseable_bounds(&generics);

let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[]);

let dummy_const = Ident::new(
&format!("_IMPL_TRAVERSEABLE_FOR_{}", ident),
Span::call_site(),
);

let gluon = match container.crate_name {
attr::CrateName::Some(ref ident) => quote! {
use #ident::gc as _gluon_gc;
},
attr::CrateName::GluonVm => quote! {
use crate::gc as _gluon_gc;
},
attr::CrateName::None => quote! {
use gluon::vm::gc as _gluon_gc;
},
};

quote! {
#[allow(non_upper_case_globals)]
const #dummy_const: () = {
#gluon

#[automatically_derived]
#[allow(unused_attributes, unused_variables)]
impl #impl_generics _gluon_gc::Traverseable for #ident #ty_generics
#where_clause #(#traverseable_bounds,)*
{
fn traverse(&self, gc: &mut _gluon_gc:: Gc) {
fn mark<T: ?Sized + _gluon_gc::Traverseable>(this: &T, gc: &mut _gluon_gc::Gc) {
_gluon_gc::Traverseable::traverse(this, gc)
}
#push_impl
}
}
};
}
}

fn gen_variant_match(ident: &Ident, variant: &Variant) -> TokenStream {
let (field_idents, _field_types) = get_info_from_fields(&variant.fields);
let field_idents2 = &field_idents;
let variant_ident = &variant.ident;

let pattern = match &variant.fields {
Fields::Named(_) => quote! { #ident::#variant_ident{ #(#field_idents2),* } },
Fields::Unnamed(_) => quote! { #ident::#variant_ident( #(#field_idents2),* ) },
Fields::Unit => quote! { #ident::#variant_ident },
};

quote! {
#pattern => {
#(mark(#field_idents2);)*
}
}
}

fn create_traverseable_bounds(generics: &Generics) -> Vec<TokenStream> {
map_type_params(generics, |ty| {
quote! {
#ty: _gluon_api::Traverseable
}
})
}

fn get_info_from_fields(fields: &Fields) -> (Vec<Cow<Ident>>, Vec<&Type>) {
// get all the fields if there are any
let fields = match fields {
Fields::Named(FieldsNamed { named, .. }) => named,
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => unnamed,
Fields::Unit => return (Vec::new(), Vec::new()),
};

fields
.iter()
.enumerate()
.map(|(idx, field)| {
// if the fields belong to a struct we use the field name,
// otherwise generate one from the index of the tuple element
let ident = match &field.ident {
Some(ident) => Cow::Borrowed(ident),
None => Cow::Owned(Ident::new(&format!("_{}", idx), Span::call_site())),
};

(ident, &field.ty)
})
.unzip()
}
16 changes: 7 additions & 9 deletions tests/compile-fail/getable-reference.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
extern crate gluon;
use gluon::new_vm;
extern crate gluon_codegen;

use gluon::import::add_extern_module;
use gluon::vm::ExternModule;
use gluon::vm::thread::{Status, Thread};
use gluon::vm::gc::{Gc, Traverseable};
use gluon::new_vm;
use gluon::vm::api::{primitive_f, Userdata, VmType};
use gluon::vm::gc::{Gc, Traverseable};
use gluon::vm::thread::{Status, Thread};
use gluon::vm::ExternModule;

#[derive(Debug)]
#[derive(Debug, gluon_codegen::Traverseable)]
struct Test;

impl Userdata for Test {}
Expand All @@ -15,10 +17,6 @@ impl VmType for Test {
type Type = Test;
}

impl Traverseable for Test {
fn traverse(&self, _: &mut Gc) {}
}

extern "C" fn dummy(_: &Thread) -> Status {
unimplemented!()
}
Expand Down
11 changes: 7 additions & 4 deletions tests/compile-fail/store-ref.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
extern crate gluon;
extern crate gluon_codegen;

use std::fmt;
use std::sync::Mutex;

use gluon::new_vm;
use gluon::import::add_extern_module;
use gluon::vm::ExternModule;
use gluon::vm::thread::{Status, Thread};
use gluon::new_vm;
use gluon::vm::api::{primitive_f, Userdata, VmType};
use gluon::vm::gc::Traverseable;
use gluon::vm::thread::{Status, Thread};
use gluon::vm::ExternModule;

#[derive(gluon_codegen::Traverseable)]
#[gluon_trace(skip)]
struct Test<'vm>(Mutex<&'vm str>);

impl Userdata for Test<'static> {}
Expand All @@ -19,7 +23,6 @@ impl<'vm> fmt::Debug for Test<'vm> {
}
}

impl<'vm> Traverseable for Test<'vm> {}
impl<'vm> VmType for Test<'vm> {
type Type = Test<'static>;
}
Expand Down
4 changes: 2 additions & 2 deletions vm/src/api/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ where
#[derive(Clone)]
struct State<'de> {
thread: &'de Thread,
env: &'de TypeEnv<Type = ArcType>,
env: &'de dyn TypeEnv<Type = ArcType>,
}

#[derive(Clone)]
Expand All @@ -212,7 +212,7 @@ struct Deserializer<'de, 't> {
impl<'de, 't> Deserializer<'de, 't> {
fn from_value(
thread: &'de Thread,
env: &'de TypeEnv<Type = ArcType>,
env: &'de dyn TypeEnv<Type = ArcType>,
input: Variants<'de>,
typ: &'t ArcType,
) -> Self {
Expand Down
16 changes: 8 additions & 8 deletions vm/src/api/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,9 @@ where
}

macro_rules! vm_function_impl {
($f:tt, $($args:ident),*) => {
([$($f:tt)*] $($args:ident),*) => {

impl <'vm, $($args,)* R> VmFunction<'vm> for $f ($($args),*) -> R
impl <'vm, $($args,)* R> VmFunction<'vm> for $($f)* ($($args),*) -> R
where $($args: Getable<'vm, 'vm> + 'vm,)*
R: AsyncPushable<'vm> + VmType + 'vm
{
Expand Down Expand Up @@ -373,22 +373,22 @@ impl <$($args: VmType,)* R: VmType> VmType for fn ($($args),*) -> R {
}
}

vm_function_impl!(fn, $($args),*);
vm_function_impl!(Fn, $($args),*);
vm_function_impl!([fn] $($args),*);
vm_function_impl!([dyn Fn] $($args),*);

impl <'vm, $($args,)* R: VmType> FunctionType for fn ($($args),*) -> R {
fn arguments() -> VmIndex {
count!($($args),*) + R::extra_args()
}
}

impl <'s, $($args,)* R: VmType> FunctionType for Fn($($args),*) -> R + 's {
impl <'s, $($args,)* R: VmType> FunctionType for dyn Fn($($args),*) -> R + 's {
fn arguments() -> VmIndex {
count!($($args),*) + R::extra_args()
}
}

impl <'s, $($args: VmType,)* R: VmType> VmType for Fn($($args),*) -> R + 's {
impl <'s, $($args: VmType,)* R: VmType> VmType for dyn Fn($($args),*) -> R + 's {
type Type = fn ($($args::Type),*) -> R::Type;

#[allow(non_snake_case)]
Expand Down Expand Up @@ -450,7 +450,7 @@ impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
pub fn call_async(
&mut self
$(, $args: $args)*
) -> Box<Future<Item = R, Error = Error> + Send + Sync + 'static>
) -> Box<dyn Future<Item = R, Error = Error> + Send + Sync + 'static>
{
use crate::thread::Execute;
use futures::IntoFuture;
Expand All @@ -477,7 +477,7 @@ impl<T, $($args,)* R> Function<T, fn($($args),*) -> R>
pub fn call_fast_async(
&mut self
$(, $args: $args)*
) -> Box<Future<Item = R, Error = Error> + Send + Sync + 'static>
) -> Box<dyn Future<Item = R, Error = Error> + Send + Sync + 'static>
{
use crate::thread::Execute;

Expand Down
4 changes: 2 additions & 2 deletions vm/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub enum ValueRef<'a> {
String(&'a str),
Data(Data<'a>),
Array(ArrayRef<'a>),
Userdata(&'a vm::Userdata),
Userdata(&'a dyn vm::Userdata),
Thread(&'a Thread),
Closure(Closure<'a>),
Internal,
Expand Down Expand Up @@ -580,7 +580,7 @@ impl<'vm, T: vm::Userdata> Pushable<'vm> for T {
fn push(self, context: &mut ActiveThread<'vm>) -> Result<()> {
let thread = context.thread();
let context = context.context();
let data: Box<vm::Userdata> = Box::new(self);
let data: Box<dyn vm::Userdata> = Box::new(self);
let userdata = context.alloc_with(thread, Move(data))?;
context.stack.push(ValueRepr::Userdata(userdata));
Ok(())
Expand Down
4 changes: 2 additions & 2 deletions vm/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ impl CompilerEnv for TypeInfos {
}

pub struct Compiler<'a> {
globals: &'a (CompilerEnv<Type = ArcType> + 'a),
globals: &'a (dyn CompilerEnv<Type = ArcType> + 'a),
vm: &'a GlobalVmState,
symbols: SymbolModule<'a>,
stack_types: ScopedMap<Symbol, Alias<Symbol, ArcType>>,
Expand Down Expand Up @@ -468,7 +468,7 @@ impl<'a, T: CompilerEnv> CompilerEnv for &'a T {

impl<'a> Compiler<'a> {
pub fn new(
globals: &'a (CompilerEnv<Type = ArcType> + 'a),
globals: &'a (dyn CompilerEnv<Type = ArcType> + 'a),
vm: &'a GlobalVmState,
mut symbols: SymbolModule<'a>,
source: &'a ::codespan::FileMap,
Expand Down
Loading

0 comments on commit 844418d

Please sign in to comment.