diff --git a/Cargo.lock b/Cargo.lock index ce333ccd38..5c494b8a2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1092,7 +1092,7 @@ dependencies = [ "futures 0.3.1", "gluon_base", "gluon_check", - "gluon_codegen 0.13.1", + "gluon_codegen", "gluon_completion", "gluon_format", "gluon_parser", @@ -1138,7 +1138,7 @@ dependencies = [ "either", "env_logger 0.7.1", "fnv", - "gluon_codegen 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "gluon_codegen", "hashbrown 0.6.3", "itertools", "log 0.4.8", @@ -1175,7 +1175,7 @@ dependencies = [ "ena", "env_logger 0.7.1", "gluon_base", - "gluon_codegen 0.13.1", + "gluon_codegen", "gluon_format", "gluon_parser", "itertools", @@ -1202,17 +1202,6 @@ dependencies = [ "syn 1.0.7", ] -[[package]] -name = "gluon_codegen" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720c5da0383b0b50795f45b29aafc53bb03671a2e7b37992002eca7d0e76fc39" -dependencies = [ - "proc-macro2 1.0.6", - "quote 1.0.2", - "syn 1.0.7", -] - [[package]] name = "gluon_completion" version = "0.13.1" @@ -1305,7 +1294,7 @@ dependencies = [ "env_logger 0.7.1", "futures 0.3.1", "gluon", - "gluon_codegen 0.13.1", + "gluon_codegen", "gluon_completion", "gluon_doc", "gluon_format", @@ -1340,7 +1329,7 @@ dependencies = [ "gluon", "gluon_base", "gluon_check", - "gluon_codegen 0.13.1", + "gluon_codegen", "gluon_parser", "itertools", "lalrpop", diff --git a/base/Cargo.toml b/base/Cargo.toml index 808c4c0a64..d014b1ba62 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -33,7 +33,7 @@ either = "1" vec_map = "0.8" typed-arena = "1" -gluon_codegen = "0.13.1" # GLUON +gluon_codegen = { version = "0.13.1", path = "../codegen" } # GLUON serde = { version = "1.0.0", features = ["rc"], optional = true } serde_state = { version = "0.4.0", features = ["rc"], optional = true } @@ -47,7 +47,6 @@ compiletest_rs = { version = "0.3.23", optional = true } env_logger = "0.7" pretty_assertions = "0.6" - [features] serialization = ["serde", "serde_state", "serde_derive", "serde_derive_state"] nightly = ["compiletest_rs"] diff --git a/base/src/ast.rs b/base/src/ast.rs index 75e579723c..216c8cfbe4 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -9,6 +9,8 @@ use std::{ use {either::Either, itertools::Itertools, ordered_float::NotNan}; +use gluon_codegen::AstClone; + #[cfg(feature = "serde")] use crate::{ serde::de::DeserializeState, @@ -74,13 +76,13 @@ impl<'a, T: ?Sized + IdentEnv> IdentEnv for &'a mut T { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct InnerAstType<'ast, Id> { metadata: Option, typ: Spanned>, BytePos>, } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct AstType<'ast, Id> { _typ: &'ast mut InnerAstType<'ast, Id>, } @@ -261,7 +263,7 @@ pub enum Literal { /// Pattern which contains a location pub type SpannedPattern<'ast, Id> = Spanned, BytePos>; -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub enum PatternField<'ast, Id> { Type { name: Spanned, @@ -280,7 +282,7 @@ impl PatternField<'_, Id> { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub enum Pattern<'ast, Id> { /// An as-pattern, eg. `option @ { monoid, functor }` As(Spanned, &'ast mut SpannedPattern<'ast, Id>), @@ -362,19 +364,19 @@ impl Default for Pattern<'_, Id> { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct Alternative<'ast, Id> { pub pattern: SpannedPattern<'ast, Id>, pub expr: SpannedExpr<'ast, Id>, } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct Array<'ast, Id> { pub typ: ArcType, pub exprs: &'ast mut [SpannedExpr<'ast, Id>], } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct Lambda<'ast, Id> { pub id: TypedIdent, pub args: &'ast mut [Argument>], @@ -389,14 +391,14 @@ pub type SpannedAlias<'ast, Id> = Spanned>, Byte pub type SpannedAstType<'ast, Id> = Spanned>, BytePos>; -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct ExprField { pub metadata: Metadata, pub name: Spanned, pub value: Option, } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct Do<'ast, Id> { pub id: Option>, pub bound: &'ast mut SpannedExpr<'ast, Id>, @@ -405,7 +407,7 @@ pub struct Do<'ast, Id> { } /// The representation of gluon's expression syntax -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub enum Expr<'ast, Id> { /// Identifiers Ident(TypedIdent), @@ -586,7 +588,7 @@ impl<'ast, Id> Expr<'ast, Id> { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct TypeBinding<'ast, Id> { pub metadata: Metadata, pub name: Spanned, @@ -600,11 +602,11 @@ impl TypeBinding<'_, Id> { } } -#[derive(Clone, Default, Eq, PartialEq, Debug, Hash)] +#[derive(Clone, Default, Eq, PartialEq, Debug, Hash, AstClone)] #[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] -pub struct Argument { +pub struct Argument { pub arg_type: ArgType, - pub name: Id, + pub name: N, } impl Argument { @@ -623,7 +625,7 @@ impl Argument { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub enum ValueBindings<'ast, Id> { Plain(&'ast mut ValueBinding<'ast, Id>), Recursive(&'ast mut [ValueBinding<'ast, Id>]), @@ -675,7 +677,7 @@ impl<'a, 'ast, Id> IntoIterator for &'a mut ValueBindings<'ast, Id> { } } -#[derive(Eq, PartialEq, Debug)] +#[derive(Eq, PartialEq, Debug, AstClone)] pub struct ValueBinding<'ast, Id> { pub metadata: Metadata, pub name: SpannedPattern<'ast, Id>, @@ -1468,3 +1470,121 @@ macro_rules! mk_ast_arena { } }; } + +pub trait AstClone<'ast, Id> { + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self; +} + +impl<'ast, Id, T> AstClone<'ast, Id> for Option +where + T: AstClone<'ast, Id>, +{ + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self { + self.as_ref().map(|x| x.ast_clone(arena)) + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for PhantomData { + fn ast_clone(&self, _arena: ArenaRef<'_, 'ast, Id>) -> Self { + PhantomData + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for Arc<[crate::types::AliasData]> +where + Id: Clone + AstClone<'ast, Id>, + T: AstClone<'ast, Id>, +{ + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self { + Arc::from(self.iter().map(|e| e.ast_clone(arena)).collect::>()) + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for Vec +where + T: Clone, +{ + fn ast_clone(&self, _arena: ArenaRef<'_, 'ast, Id>) -> Self { + self.clone() + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for crate::types::AppVec +where + T: Clone, +{ + fn ast_clone(&self, _arena: ArenaRef<'_, 'ast, Id>) -> Self { + self.clone() + } +} + +impl<'ast, Id, T, P> AstClone<'ast, Id> for Spanned +where + T: AstClone<'ast, Id>, + P: Clone, +{ + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self { + pos::spanned(self.span.clone(), self.value.ast_clone(arena)) + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for TypedIdent +where + Id: Clone, + T: AstClone<'ast, Id>, +{ + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self { + TypedIdent { + name: self.name.clone(), + typ: self.typ.ast_clone(arena), + } + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for &'ast mut [T] +where + T: AstClone<'ast, Id> + AstAlloc<'ast, Id>, +{ + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self { + let elems: Vec<_> = self.iter().map(|e| e.ast_clone(arena)).collect(); + arena.alloc_extend(elems) + } +} + +impl<'ast, Id, T> AstClone<'ast, Id> for &'ast mut T +where + T: AstClone<'ast, Id> + AstAlloc<'ast, Id>, +{ + fn ast_clone(&self, arena: ArenaRef<'_, 'ast, Id>) -> Self { + arena.alloc((**self).ast_clone(arena)) + } +} + +macro_rules! impl_ast_clone { + ($($ty: ty $(where [$($where_: tt)*])? ,)*) => { + $( + impl<'ast, Id> AstClone<'ast, Id> for $ty + $( where $($where_)* )? + { + fn ast_clone(&self, _arena: ArenaRef<'_, 'ast, Id>) -> Self { + self.clone() + } + } + )* + }; +} + +impl_ast_clone! { + ArcType, + Literal, + Metadata, + crate::types::TypeVariable, + ArcKind, + crate::types::ArgType, + crate::types::BuiltinType, + usize, + u32, + bool, + BytePos, + Symbol, +} diff --git a/base/src/pos.rs b/base/src/pos.rs index ecea7acb95..57ca86fd19 100644 --- a/base/src/pos.rs +++ b/base/src/pos.rs @@ -86,10 +86,7 @@ where Span::new(start, end) } -pub fn spanned(span: Span, value: T) -> Spanned -where - Pos: Ord, -{ +pub fn spanned(span: Span, value: T) -> Spanned { Spanned { span, value } } diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 6541bb16fa..d8ea8a1c51 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -19,8 +19,10 @@ use { smallvec::SmallVec, }; +use gluon_codegen::AstClone; + use crate::{ - ast::{EmptyEnv, HasMetadata, IdentEnv}, + ast::{AstClone, EmptyEnv, HasMetadata, IdentEnv}, fnv::FnvMap, kind::{ArcKind, Kind, KindCache, KindEnv}, merge::{merge, merge_collect}, @@ -284,7 +286,7 @@ pub struct TypeVariable { forward_eq_hash! { <> for TypeVariable { id } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr( @@ -310,7 +312,7 @@ forward_eq_hash! { for Skolem { id } } /// FIXME Distinguish generic id's so we only need to compare them by `id` (currently they will get /// the wrong kind assigned to them otherwise) -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr( @@ -339,7 +341,7 @@ impl Generic { /// An alias is wrapper around `Type::Alias`, allowing it to be cheaply converted to a type and /// dereferenced to `AliasRef` -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr( @@ -517,7 +519,7 @@ where /// Data for a type alias. Probably you want to use `Alias` instead of this directly as Alias allows /// for cheap conversion back into a type as well. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr( @@ -643,7 +645,7 @@ where } } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr( @@ -751,7 +753,7 @@ where } } -#[derive(Clone, Hash, Eq, PartialEq, Debug)] +#[derive(Clone, Hash, Eq, PartialEq, Debug, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr(feature = "serde_derive", serde(de_parameters = "U"))] @@ -838,7 +840,7 @@ impl Default for ArgType { /// `Type` is used to enable types to be shared. It is recommended to use the static functions on /// `Type` such as `Type::app` and `Type::record` when constructing types as those will construct /// the pointer wrapper directly. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, AstClone)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState, SerializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "Seed"))] #[cfg_attr( @@ -867,6 +869,10 @@ impl Default for ArgType { )) )] #[cfg_attr(feature = "serde_derive", serde(serialize_state = "SeSeed"))] +#[gluon(ast_clone_bounds = "T::Generics: AstClone<'ast, Id>, + T::Types: AstClone<'ast, Id>, + T::Fields: AstClone<'ast, Id>, + T::TypeFields: AstClone<'ast, Id>")] pub enum Type = ArcType> { /// An unbound type `_`, awaiting ascription. Hole, diff --git a/codegen/src/ast_clone.rs b/codegen/src/ast_clone.rs new file mode 100644 index 0000000000..f985dc8499 --- /dev/null +++ b/codegen/src/ast_clone.rs @@ -0,0 +1,272 @@ +use std::str::FromStr; + +use { + proc_macro2::{Span, TokenStream}, + syn::{ + self, Data, DataEnum, DataStruct, DeriveInput, Field, Fields, FieldsNamed, FieldsUnnamed, + Generics, Ident, Variant, + }, +}; + +use crate::{ + attr, + shared::{map_lifetimes, map_type_params, split_for_impl}, +}; + +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(ident: &Ident, fields: I) -> TokenStream +where + I: IntoIterator, +{ + // lookup each field by its name and then convert to its type using the Getable + // impl of the fields type + let field_initializers = fields.into_iter().map(|field| { + let field_ty = &field.ty; + let ident = field + .ident + .as_ref() + .expect("Struct fields always have names"); + + quote! { + #ident: <#field_ty as gluon_base::ast::AstClone<'ast, Id>>::ast_clone(&self.#ident, arena) + } + }); + + quote! { + + #ident { + #(#field_initializers,)* + } + } +} + +fn gen_tuple_struct_cons(ident: &Ident, fields: I) -> TokenStream +where + I: IntoIterator, +{ + let mut fields = fields.into_iter().fuse(); + + // Treat newtype structs as just their inner type + let (first, second) = (fields.next(), fields.next()); + match (&first, &second) { + (Some(field), None) => { + let field_ty = &field.ty; + return quote! { + #ident ( + <#field_ty as gluon_base::ast::AstClone<'__vm, _>>::ast_clone(&self.0, arena) + ) + }; + } + _ => (), + } + + // do the lookup using the tag, because tuple structs don't have field names + let field_initializers = first + .into_iter() + .chain(second) + .chain(fields) + .enumerate() + .map(|(idx, field)| { + let field_ty = &field.ty; + let idx = syn::Index::from(idx); + + quote! { + <#field_ty as gluon_base::ast::AstClone<'__vm, _>>::ast_clone(&self. #idx, arena) + } + }); + + quote! { + + #ident ( + #(#field_initializers,)* + ) + } +} + +fn derive_enum( + container: &attr::Container, + ast: DataEnum, + ident: Ident, + generics: Generics, +) -> TokenStream { + let cons = { + let variants = ast + .variants + .iter() + .enumerate() + .map(|(tag, variant)| gen_variant_match(&ident, tag, variant)); + + // data contains the the data for each field of a variant; the variant of the passed value + // is defined by the tag(), which is defined by order of the variants (the first variant is 0) + quote! { + match self { + #(#variants,)* + } + } + }; + + gen_impl(container, ident, generics, cons) +} + +fn gen_impl( + container: &attr::Container, + ident: Ident, + generics: Generics, + clone_impl: TokenStream, +) -> TokenStream { + // lifetime bounds like '__vm: 'a, 'a: '__vm (which implies => 'a == '__vm) + // writing bounds like this is a lot easier than actually replacing all lifetimes + // with '__vm + let lifetime_bounds = create_lifetime_bounds(&generics); + + // generate bounds like T: Getable for every type parameter + let ast_clone_bounds = create_ast_clone_bounds(&generics); + + let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &["Id"], &["'ast"]); + + let dummy_const = Ident::new(&format!("_IMPL_AST_CLONE_FOR_{}", ident), Span::call_site()); + + let extra_bounds = container.ast_clone_bounds.as_ref().map(|b| { + let b = TokenStream::from_str(b).unwrap(); + quote! { #b, } + }); + + quote! { + #[allow(non_upper_case_globals)] + const #dummy_const: () = { + use crate as gluon_base; + + #[automatically_derived] + #[allow(unused_attributes, unused_variables)] + impl #impl_generics gluon_base::ast::AstClone<'ast, Id> for #ident #ty_generics + #where_clause #(#ast_clone_bounds,)* #(#lifetime_bounds),* #extra_bounds Id: Clone + { + fn ast_clone(&self, arena: gluon_base::ast::ArenaRef<'_, 'ast, Id>) -> Self { + #clone_impl + } + } + }; + } +} + +fn gen_variant_match(ident: &Ident, _tag: usize, variant: &Variant) -> TokenStream { + let variant_ident = &variant.ident; + + // depending on the type of the variant we need to generate different constructors + // for the enum + match &variant.fields { + Fields::Unit => quote! { + #ident::#variant_ident => #ident::#variant_ident + }, + // both constructors that need to marshall values extract them by using the index + // of the field to get the content from Data::get_variant; + // the data variable was assigned in the function body above + Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { + let fields: Vec<_> = unnamed + .iter() + .enumerate() + .map(|(idx, _field)| syn::Ident::new(&format!("_{}", idx), Span::call_site())) + .collect(); + let cons = gen_tuple_variant_cons(unnamed.iter().zip(fields.iter())); + + quote! { + #ident::#variant_ident ( #(#fields),* ) => + #ident::#variant_ident#cons + } + } + Fields::Named(FieldsNamed { named, .. }) => { + let cons = gen_struct_variant_cons(ident, variant_ident, named); + let named = named.iter().map(|field| field.ident.as_ref().unwrap()); + + quote! { + #ident::#variant_ident { #(#named),* } => #cons + } + } + } +} + +fn gen_tuple_variant_cons<'a, I>(fields: I) -> TokenStream +where + I: IntoIterator, +{ + let fields = fields.into_iter().map(|(field, ident)| { + let field_ty = &field.ty; + + quote! { + <#field_ty as gluon_base::ast::AstClone<_>>::ast_clone(#ident, arena) + } + }); + + quote! { + (#(#fields),*) + } +} + +fn gen_struct_variant_cons<'a, I>(ident: &Ident, variant_ident: &Ident, fields: I) -> TokenStream +where + I: IntoIterator, +{ + let fields = fields.into_iter().map(|field| { + let field_ty = &field.ty; + let field_ident = field + .ident + .as_ref() + .expect("Struct fields always have names"); + quote! { + #field_ident: <#field_ty as gluon_base::ast::AstClone<_>>::ast_clone(#field_ident, arena) + } + }); + + quote! { + #ident::#variant_ident { #(#fields),* } + } +} + +fn create_ast_clone_bounds(generics: &Generics) -> Vec { + map_type_params(generics, |ty| { + quote! { + #ty: gluon_base::ast::AstClone<'ast, Id> + } + }) +} + +fn create_lifetime_bounds(generics: &Generics) -> Vec { + map_lifetimes(generics, |_lifetime| { + quote! {} + }) +} diff --git a/codegen/src/attr.rs b/codegen/src/attr.rs index d1be106b51..5c37b9a706 100644 --- a/codegen/src/attr.rs +++ b/codegen/src/attr.rs @@ -34,6 +34,7 @@ pub struct Container { pub newtype: bool, pub skip: bool, pub clone: bool, + pub ast_clone_bounds: Option, } impl Container { @@ -45,6 +46,7 @@ impl Container { let mut newtype = false; let mut skip = false; let mut clone = false; + let mut ast_clone_bounds = None; for meta_items in item.attrs.iter().filter_map(get_gluon_meta_items) { for meta_item in meta_items { @@ -77,6 +79,11 @@ impl Container { clone = true; } + Meta(NameValue(ref m)) if m.path.is_ident("ast_clone_bounds") => { + ast_clone_bounds = + Some(get_lit_str(&m.path, &m.path, &m.lit).unwrap().value()) + } + _ => panic!("unexpected gluon container attribute: {:?}", meta_item), } } @@ -88,6 +95,7 @@ impl Container { newtype, skip, clone, + ast_clone_bounds, } } } diff --git a/codegen/src/functor.rs b/codegen/src/functor.rs index 24c684bf4c..0357092247 100644 --- a/codegen/src/functor.rs +++ b/codegen/src/functor.rs @@ -23,7 +23,7 @@ pub fn derive(input: TokenStream) -> TokenStream { fn gen_impl(ident: Ident, generics: Generics, data: &Data) -> TokenStream { assert!(generics.type_params().count() > 0); - let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[]); + let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &[]); let map_methods = generics .type_params() diff --git a/codegen/src/getable.rs b/codegen/src/getable.rs index bbe26c6bf7..6fdcb0aff2 100644 --- a/codegen/src/getable.rs +++ b/codegen/src/getable.rs @@ -172,7 +172,7 @@ fn gen_impl( let getable_bounds = create_getable_bounds(&generics); let (impl_generics, ty_generics, where_clause) = - split_for_impl(&generics, &["'__vm", "'__value"]); + split_for_impl(&generics, &[], &["'__vm", "'__value"]); let dummy_const = Ident::new(&format!("_IMPL_GETABLE_FOR_{}", ident), Span::call_site()); diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4ce226795f..af11642807 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -162,6 +162,7 @@ mod pushable; mod shared; mod trace; mod userdata; +mod ast_clone; mod vm_type; #[doc(hidden)] @@ -199,3 +200,10 @@ pub fn trace(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn functor(input: proc_macro::TokenStream) -> proc_macro::TokenStream { functor::derive(input.into()).into() } + +#[doc(hidden)] +#[proc_macro_derive(AstClone, attributes(gluon))] +pub fn ast_clone(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + ast_clone::derive(input.into()).into() +} + diff --git a/codegen/src/pushable.rs b/codegen/src/pushable.rs index 2dab186545..89efb0b558 100644 --- a/codegen/src/pushable.rs +++ b/codegen/src/pushable.rs @@ -128,7 +128,7 @@ fn gen_impl( push_impl: TokenStream, ) -> TokenStream { let pushable_bounds = create_pushable_bounds(&generics); - let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &["'__vm"]); + let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &["'__vm"]); let dummy_const = Ident::new(&format!("_IMPL_PUSHABLE_FOR_{}", ident), Span::call_site()); diff --git a/codegen/src/shared.rs b/codegen/src/shared.rs index cd4dadb1a9..1cd90bcea2 100644 --- a/codegen/src/shared.rs +++ b/codegen/src/shared.rs @@ -1,5 +1,5 @@ use proc_macro2::{Ident, Span, TokenStream}; -use syn::{GenericParam, Generics, Lifetime, LifetimeDef, TypeGenerics}; +use syn::{GenericParam, Generics, Lifetime, LifetimeDef, TypeGenerics, TypeParam}; /// Maps all type parameters in `generics`. The function gets passed the ident of /// the respective type parameter. @@ -44,6 +44,7 @@ where /// definition and can thus be freely used for the trait that is implemented. pub fn split_for_impl<'a>( generics: &'a Generics, + extra_params: &[&str], extra_lifetimes: &[&str], ) -> (TokenStream, TypeGenerics<'a>, TokenStream) { // add trailing comma or add a where if there are no predicates @@ -57,12 +58,25 @@ pub fn split_for_impl<'a>( // these need the additional lifetimes let mut generics = generics.clone(); + extra_params + .into_iter() + .map(|param| GenericParam::from(TypeParam::from(Ident::new(param, Span::call_site())))) + .for_each(|param| { + if generics.params.iter().all(|p| *p != param) { + generics.params.insert(0, param) + } + }); + extra_lifetimes .into_iter() .map(|lifetime| { GenericParam::from(LifetimeDef::new(Lifetime::new(lifetime, Span::call_site()))) }) - .for_each(|lifetime| generics.params.insert(0, lifetime)); + .for_each(|lifetime| { + if generics.params.iter().all(|p| *p != lifetime) { + generics.params.insert(0, lifetime) + } + }); let (impl_generics, ..) = generics.split_for_impl(); (quote! { #impl_generics }, ty_generics, where_clause) diff --git a/codegen/src/trace.rs b/codegen/src/trace.rs index 7e174a8237..ce45f09bd0 100644 --- a/codegen/src/trace.rs +++ b/codegen/src/trace.rs @@ -123,7 +123,7 @@ fn gen_impl( // generate bounds like T: Getable for every type parameter let trace_bounds = create_trace_bounds(&generics); - let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[]); + let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &[]); let dummy_const = Ident::new( &format!("_IMPL_TRAVERSEABLE_FOR_{}", ident), diff --git a/codegen/src/userdata.rs b/codegen/src/userdata.rs index 21761b51bd..cc23cef68b 100644 --- a/codegen/src/userdata.rs +++ b/codegen/src/userdata.rs @@ -33,7 +33,7 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics) -> TokenStr let lifetime_bounds = &map_lifetimes(&generics, |lifetime| quote! { #lifetime: 'static }); - let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[]); + let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &[]); let gluon = match container.crate_name { CrateName::Some(ref ident) => quote! { diff --git a/codegen/src/vm_type.rs b/codegen/src/vm_type.rs index f1199f3568..47f3673592 100644 --- a/codegen/src/vm_type.rs +++ b/codegen/src/vm_type.rs @@ -31,7 +31,7 @@ fn gen_impl(container: &Container, ident: Ident, generics: Generics, data: &Data quote! { #ty: _gluon_api::VmType, #ty::Type: Sized } }); - let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[]); + let (impl_generics, ty_generics, where_clause) = split_for_impl(&generics, &[], &[]); let gluon = match container.crate_name { CrateName::Some(ref ident) => quote! { diff --git a/src/lift_io.rs b/src/lift_io.rs index 4b53305618..d22287a6ab 100644 --- a/src/lift_io.rs +++ b/src/lift_io.rs @@ -1,11 +1,9 @@ -use std::mem; - use gluon_codegen::Trace; use { base::{ ast::{ - self, Argument, AstType, EmptyEnv, Expr, ExprField, Pattern, SpannedExpr, Typed, + self, AstClone, Argument, AstType, EmptyEnv, Expr, ExprField, Pattern, SpannedExpr, Typed, TypedIdent, ValueBinding, }, pos::{self, BytePos, Span}, @@ -61,7 +59,7 @@ impl Macro for LiftIo { } } - let module = mem::take(module); + let module = (*module).ast_clone(arena.borrow()); let span = module.span; let vm = env.vm; diff --git a/std/effect/io/read.glu b/std/effect/io/read.glu index baeb0924ff..dc569344e3 100644 --- a/std/effect/io/read.glu +++ b/std/effect/io/read.glu @@ -1,3 +1,3 @@ let { lift } = import! std.effect.lift let { Read } = import! std.io.read -lift_io! lift (import! std.io.read) +lift_io! lift