diff --git a/base/src/ast.rs b/base/src/ast.rs index 7d36a4ee2c..868e6972cd 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -181,7 +181,7 @@ impl AsMut> for AstType { } } -#[derive(Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, Default)] pub struct TypedIdent> { pub typ: T, pub name: Id, diff --git a/base/src/merge.rs b/base/src/merge.rs index 79ace1e975..2084c86d42 100644 --- a/base/src/merge.rs +++ b/base/src/merge.rs @@ -74,10 +74,10 @@ where T: Clone + 'a, R: std::iter::FromIterator, { - merge_iter(types, |(l, r)| f(l, r), |(l, _)| l.clone()) + merge_collect(types, |(l, r)| f(l, r), |(l, _)| l.clone()) } -struct MergeIter { +pub struct MergeIter { types: I, clone_types_iter: I, action: F, @@ -123,7 +123,18 @@ where } } -pub fn merge_iter(types: I, action: F, converter: G) -> Option +impl ExactSizeIterator for MergeIter +where + I: ExactSizeIterator, + F: FnMut(I::Item) -> Option, + G: FnMut(I::Item) -> U, +{ + fn len(&self) -> usize { + self.clone_types_iter.len() + } +} + +pub fn merge_collect(types: I, action: F, converter: G) -> Option where I: IntoIterator, I::IntoIter: FusedIterator + Clone, @@ -131,10 +142,10 @@ where G: FnMut(I::Item) -> U, R: std::iter::FromIterator, { - merge_iter_(types, action, converter).map(|iter| iter.collect()) + merge_iter(types, action, converter).map(|iter| iter.collect()) } -fn merge_iter_( +pub fn merge_iter( types: I, mut action: F, converter: G, diff --git a/base/src/symbol.rs b/base/src/symbol.rs index d2aec429ba..c57389748a 100644 --- a/base/src/symbol.rs +++ b/base/src/symbol.rs @@ -11,7 +11,7 @@ use crate::ast::{DisplayEnv, IdentEnv}; // FIXME Don't have a double indirection (Arc + String) /// A symbol uniquely identifies something regardless of its name and which module it originated /// from -#[derive(Clone, Eq)] +#[derive(Clone, Eq, Default)] pub struct Symbol(Arc); #[derive(Debug, Eq, PartialEq, Hash)] @@ -297,7 +297,7 @@ impl SymbolRef { } } -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[cfg_attr(feature = "serde_derive", derive(DeserializeState))] #[cfg_attr(feature = "serde_derive", serde(deserialize_state = "S"))] #[cfg_attr(feature = "serde_derive", serde(de_parameters = "S"))] @@ -504,7 +504,7 @@ impl Symbols { location: None, name, }) - } + } /// Looks up the symbol for `name` or creates a new symbol if it does not exist pub fn symbol(&mut self, name: SymbolData) -> Symbol diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 23838e608b..b3569c1469 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -22,7 +22,7 @@ use crate::{ ast::{Commented, EmptyEnv, IdentEnv}, fnv::FnvMap, kind::{ArcKind, Kind, KindCache, KindEnv}, - merge::{merge, merge_iter}, + merge::{merge, merge_collect}, metadata::Comment, pos::{BytePos, HasSpan, Span}, source::Source, @@ -3864,7 +3864,7 @@ where T: Clone + 'a, R: std::iter::FromIterator, { - merge_iter(types, f, Clone::clone) + merge_collect(types, f, Clone::clone) } pub fn translate_alias(alias: &AliasData, mut translate: F) -> AliasData diff --git a/vm/src/core/interpreter.rs b/vm/src/core/interpreter.rs index 4159548f15..bb7f7b53db 100644 --- a/vm/src/core/interpreter.rs +++ b/vm/src/core/interpreter.rs @@ -4,7 +4,7 @@ use crate::base::{ ast::TypedIdent, fnv::FnvSet, kind::{ArcKind, KindEnv}, - merge::merge_iter, + merge::merge_collect, scoped_map::ScopedMap, symbol::{Symbol, SymbolRef}, types::{Alias, ArcType, TypeEnv, TypeExt}, @@ -486,7 +486,7 @@ impl<'a, 'e> Compiler<'a, 'e> { ); } let mut result = Ok(()); - let new_closures = merge_iter( + let new_closures = merge_collect( closures, |closure| { function.stack.enter_scope(); diff --git a/vm/src/core/mod.rs b/vm/src/core/mod.rs index db25a2de7e..6f8ecfe3ba 100644 --- a/vm/src/core/mod.rs +++ b/vm/src/core/mod.rs @@ -65,7 +65,13 @@ pub enum Named<'a> { Expr(&'a Expr<'a>), } -#[derive(Clone, Debug, PartialEq)] +impl<'a> Default for Named<'a> { + fn default() -> Self { + Named::Recursive(Vec::new()) + } +} + +#[derive(Clone, Debug, PartialEq, Default)] pub struct LetBinding<'a> { pub name: TypedIdent, pub expr: Named<'a>, @@ -329,6 +335,53 @@ impl<'a> Allocator<'a> { } } +pub trait ArenaAllocatable<'a>: Sized { + fn alloc_into(self, allocator: &'a Allocator<'a>) -> &'a Self; + fn alloc_iter_into( + iter: impl IntoIterator, + allocator: &'a Allocator<'a>, + ) -> &'a [Self]; +} + +impl<'a> ArenaAllocatable<'a> for Expr<'a> { + fn alloc_into(self, allocator: &'a Allocator<'a>) -> &'a Self { + allocator.arena.alloc(self) + } + + fn alloc_iter_into( + iter: impl IntoIterator, + allocator: &'a Allocator<'a>, + ) -> &'a [Self] { + allocator.arena.alloc_fixed(iter) + } +} + +impl<'a> ArenaAllocatable<'a> for Alternative<'a> { + fn alloc_into(self, allocator: &'a Allocator<'a>) -> &'a Self { + allocator.alternative_arena.alloc(self) + } + + fn alloc_iter_into( + iter: impl IntoIterator, + allocator: &'a Allocator<'a>, + ) -> &'a [Self] { + allocator.alternative_arena.alloc_fixed(iter) + } +} + +impl<'a> ArenaAllocatable<'a> for LetBinding<'a> { + fn alloc_into(self, allocator: &'a Allocator<'a>) -> &'a Self { + allocator.let_binding_arena.alloc(self) + } + + fn alloc_iter_into( + iter: impl IntoIterator, + allocator: &'a Allocator<'a>, + ) -> &'a [Self] { + allocator.let_binding_arena.alloc_fixed(iter) + } +} + pub(crate) trait ArenaExt { fn alloc_fixed<'a, I>(&'a self, iter: I) -> &'a mut [T] where diff --git a/vm/src/core/optimize.rs b/vm/src/core/optimize.rs index aec7b04f7b..f44c250e15 100644 --- a/vm/src/core/optimize.rs +++ b/vm/src/core/optimize.rs @@ -2,17 +2,52 @@ use std::marker::PhantomData; use crate::base::{ ast::TypedIdent, - merge::{merge_fn, merge_iter}, + merge::{merge_collect, merge_fn, merge_iter}, pos, symbol::Symbol, types::{ArcType, Field, TypeEnv, TypeExt}, }; -use crate::core::{Allocator, Alternative, CExpr, Closure, Expr, LetBinding, Named, Pattern}; +use crate::core::{ + Allocator, Alternative, ArenaAllocatable, ArenaExt, CExpr, Closure, Expr, LetBinding, Named, + Pattern, +}; + +pub trait Produce<'a, 'b, P, Input> { + fn produce_with(input: &'b Input, producer: &mut P) -> Self; +} + +impl<'a, 'b, P> Produce<'a, 'b, P, Expr<'b>> for Expr<'a> +where + P: ExprProducer<'a, 'b>, +{ + fn produce_with(input: &'b Expr<'b>, producer: &mut P) -> Self { + producer.produce(input).clone() + } +} + +impl<'a, 'b, P> Produce<'a, 'b, P, Expr<'b>> for CExpr<'a> +where + P: ExprProducer<'a, 'b>, +{ + fn produce_with(input: &'b Expr<'b>, producer: &mut P) -> Self { + producer.produce(input) + } +} + +impl<'a, 'b, P> Produce<'a, 'b, P, Alternative<'b>> for Alternative<'a> +where + P: ExprProducer<'a, 'b>, +{ + fn produce_with(input: &'b Alternative<'b>, producer: &mut P) -> Self { + producer.produce_alt(input) + } +} pub trait ExprProducer<'a, 'b>: Visitor<'a, 'b> { fn new(allocator: &'a Allocator<'a>) -> Self; fn produce(&mut self, expr: CExpr<'b>) -> CExpr<'a>; + fn produce_alt(&mut self, alt: &'b Alternative<'b>) -> Alternative<'a>; } pub struct SameLifetime<'a>(&'a Allocator<'a>); @@ -23,6 +58,9 @@ impl<'a> ExprProducer<'a, 'a> for SameLifetime<'a> { fn produce(&mut self, expr: CExpr<'a>) -> CExpr<'a> { expr } + fn produce_alt(&mut self, alt: &'a Alternative<'a>) -> Alternative<'a> { + alt.clone() + } } impl<'a> Visitor<'a, 'a> for SameLifetime<'a> { @@ -56,6 +94,12 @@ impl<'a, 'b> ExprProducer<'a, 'b> for DifferentLifetime<'a, 'b> { _ => walk_expr_alloc(self, expr).unwrap(), } } + fn produce_alt(&mut self, alt: &'b Alternative<'b>) -> Alternative<'a> { + Alternative { + pattern: alt.pattern.clone(), + expr: self.produce(alt.expr), + } + } } impl<'a, 'b> Visitor<'a, 'b> for DifferentLifetime<'a, 'b> { @@ -74,9 +118,19 @@ pub trait Visitor<'a, 'b> { type Producer: ExprProducer<'a, 'b>; fn visit_expr(&mut self, expr: CExpr<'b>) -> Option<&'a Expr<'a>>; + fn visit_expr_(&mut self, expr: CExpr<'b>) -> Option> { self.visit_expr(expr).map(Clone::clone) } + + fn visit_alt(&mut self, alt: &'b Alternative<'b>) -> Option> { + let new_expr = self.visit_expr(alt.expr); + new_expr.map(|expr| Alternative { + pattern: alt.pattern.clone(), + expr: expr, + }) + } + fn detach_allocator(&self) -> Option<&'a Allocator<'a>>; fn allocator(&self) -> &'a Allocator<'a> { self.detach_allocator().expect("Allocator") @@ -192,20 +246,13 @@ pub fn walk_expr<'a, 'b, V>(visitor: &mut V, expr: CExpr<'b>) -> Option where V: ?Sized + Visitor<'a, 'b>, { - let allocator = visitor.detach_allocator(); + let allocator: Option<&'a Allocator<'a>> = visitor.detach_allocator(); match *expr { Expr::Call(f, args) => { let new_f = visitor.visit_expr(f); - let new_args = merge_iter( - args, - |expr| visitor.visit_expr_(expr), - |e| { - V::Producer::new(allocator.expect("Allocator")) - .produce(e) - .clone() - }, - ) - .map(|exprs: Vec<_>| &*visitor.allocator().arena.alloc_extend(exprs.into_iter())); + let new_args = merge_slice_produce::(allocator, args, |expr| { + visitor.visit_expr_(expr) + }); merge_fn( &f, @@ -215,31 +262,20 @@ where |a| { let a = a .iter() - .map(|a| V::Producer::new(visitor.allocator()).produce(a).clone()) - .collect::>(); - visitor.allocator().arena.alloc_extend(a) + .map(|a| V::Producer::new(visitor.allocator()).produce(a).clone()); + &*visitor.allocator().arena.alloc_fixed(a) }, new_args, Expr::Call, ) } Expr::Const(_, _) | Expr::Ident(_, _) => None, - Expr::Data(ref id, exprs, pos) => merge_iter( - exprs, - |expr| visitor.visit_expr_(expr), - |e| { - V::Producer::new(allocator.expect("Allocator")) - .produce(e) - .clone() - }, - ) - .map(|exprs: Vec<_>| { - Expr::Data( - id.clone(), - visitor.allocator().arena.alloc_extend(exprs.into_iter()), - pos, - ) - }), + Expr::Data(ref id, exprs, pos) => { + merge_slice_produce::(allocator, exprs, |expr| { + visitor.visit_expr_(expr) + }) + .map(|exprs| Expr::Data(id.clone(), exprs, pos)) + } Expr::Let(ref bind, expr) => { let new_bind = walk_bind(visitor, bind); let new_expr = visitor.visit_expr(expr); @@ -255,19 +291,8 @@ where } Expr::Match(expr, alts) => { let new_expr = visitor.visit_expr(expr); - let new_alts = merge_iter( - alts, - |expr| walk_alt(visitor, expr), - |alt| { - walk_alt(&mut V::Producer::new(allocator.expect("Allocator")), alt) - .expect("alt") - }, - ) - .map(|alts: Vec<_>| { - &*visitor - .allocator() - .alternative_arena - .alloc_extend(alts.into_iter()) + let new_alts = merge_slice_produce::(allocator, alts, |alt| { + visitor.visit_alt(alt) }); merge_fn( &expr, @@ -275,14 +300,11 @@ where new_expr, &alts, |a| { - let a = a - .iter() - .map(|a| Alternative { - pattern: a.pattern.clone(), - expr: V::Producer::new(visitor.allocator()).produce(a.expr), - }) - .collect::>(); - visitor.allocator().alternative_arena.alloc_extend(a) + let a = a.iter().map(|a| Alternative { + pattern: a.pattern.clone(), + expr: V::Producer::new(visitor.allocator()).produce(a.expr), + }); + visitor.allocator().alternative_arena.alloc_fixed(a) }, new_alts, Expr::Match, @@ -297,7 +319,7 @@ where { let allocator = visitor.detach_allocator(); let new_named = match bind.expr { - Named::Recursive(ref closures) => merge_iter( + Named::Recursive(ref closures) => merge_collect( closures, |closure| { visitor.visit_expr(closure.expr).map(|new_expr| Closure { @@ -329,15 +351,33 @@ where }) } -fn walk_alt<'a, 'b, V>(visitor: &mut V, alt: &'b Alternative<'b>) -> Option> +pub fn merge_slice<'a, T, U>( + allocator: &'a Allocator<'a>, + slice: &'a [U], + action: impl FnMut(&'a U) -> Option, +) -> Option<&'a [T]> where - V: ?Sized + Visitor<'a, 'b>, + U: ArenaAllocatable<'a> + 'a, + T: ArenaAllocatable<'a> + Produce<'a, 'a, SameLifetime<'a>, U>, +{ + merge_slice_produce::(Some(allocator), slice, action) +} + +pub fn merge_slice_produce<'a, 'b, P, T, U, F>( + allocator: Option<&'a Allocator<'a>>, + slice: &'b [U], + action: F, +) -> Option<&'a [T]> +where + U: ArenaAllocatable<'b> + 'b, + T: ArenaAllocatable<'a> + Produce<'a, 'b, P, U>, + P: ExprProducer<'a, 'b>, + F: FnMut(&'b U) -> Option, { - let new_expr = visitor.visit_expr(alt.expr); - new_expr.map(|expr| Alternative { - pattern: alt.pattern.clone(), - expr: expr, + merge_iter(slice, action, |e| { + T::produce_with(e, &mut P::new(allocator.expect("Allocator"))) }) + .map(|iter| ArenaAllocatable::alloc_iter_into(iter, allocator.expect("Allocator"))) } #[cfg(all(test, feature = "test"))]