From c2d14e598bc16f5f643a9d06e68b5d7ee5bb0ff7 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 10 Aug 2019 10:18:38 +0200 Subject: [PATCH 01/27] refactor(check): Avoid unnecessary recursion --- check/src/typecheck.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index b5cc742f62..993bbb3ffc 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -1632,7 +1632,7 @@ impl<'a> Typecheck<'a> { ); } - match self.typecheck_pattern_rec(args, ctor_type) { + match self.typecheck_pattern_rec(args, &ctor_type) { Ok(return_type) => return_type, Err(err) => self.error(span, err), } @@ -1789,19 +1789,19 @@ impl<'a> Typecheck<'a> { fn typecheck_pattern_rec( &mut self, args: &mut [SpannedPattern], - typ: RcType, + mut typ: &RcType, ) -> TcResult { let len = args.len(); - match args.split_first_mut() { - Some((head, tail)) => match typ.as_function() { + for arg_pattern in args { + match typ.as_function() { Some((arg, ret)) => { - self.typecheck_pattern(head, ModType::wobbly(arg.clone()), arg.clone()); - self.typecheck_pattern_rec(tail, ret.clone()) + self.typecheck_pattern(arg_pattern, ModType::wobbly(arg.clone()), arg.clone()); + typ = ret; } - None => Err(TypeError::PatternError(typ.clone(), len)), - }, - None => Ok(typ), + None => return Err(TypeError::PatternError(typ.clone(), len)), + } } + Ok(typ.clone()) } fn translate_projected_type(&mut self, id: &[Symbol]) -> TcResult { From 06ef80df7789d0cf141dba6248627ab63a624999 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 10 Aug 2019 10:20:34 +0200 Subject: [PATCH 02/27] refactor(check): Generate more straightforward code for subsumption --- check/src/unify_type.rs | 63 +++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 0a2e7ee75d..6333f7b2b7 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -1361,7 +1361,6 @@ impl<'a, 'e> Unifier, RcType> for UnifierState<'a, Subsume<'e>> { r: &RcType, ) -> Result, UnifyError, RcType>> { let mut subs = self.unifier.subs; - // Retrieve the 'real' types by resolving let l = subs.real(l); let r = subs.real(r); debug!("{} <=> {}", l, r); @@ -1369,57 +1368,67 @@ impl<'a, 'e> Unifier, RcType> for UnifierState<'a, Subsume<'e>> { // unified with whatever the other type is match (&**l, &**r) { (&Type::Hole, _) => Ok(Some(r.clone())), - (&Type::Variable(ref l), &Type::Variable(ref r)) if l.id == r.id => Ok(None), - (_, &Type::Forall(ref params, ref r)) => { - let mut variables = params - .iter() - .map(|param| (param.id.clone(), subs.new_var())) - .collect(); - let r = r.instantiate_generics(&mut subs, &mut variables); - self.try_match_res(l, &r) - } - - (_, &Type::Variable(_)) => { + (_, Type::Variable(_)) => { debug!("Union merge {} <> {}", l, r); subs.union(r, l)?; Ok(None) } - (&Type::Variable(_), _) => { + (Type::Variable(_), _) => { debug!("Union merge {} <> {}", l, r); subs.union(l, r)?; Ok(None) } - (&Type::Skolem(_), _) if self.state.refinement => { + (Type::Skolem(_), _) if self.state.refinement => { debug!("Skolem union {} <> {}", l, r); subs.union(l, r)?; Ok(None) } - (_, &Type::Skolem(_)) if self.state.refinement => { + (_, Type::Skolem(_)) if self.state.refinement => { debug!("Skolem union {} <> {}", l, r); subs.union(r, l)?; Ok(None) } - (&Type::Forall(_, _), _) => Ok(self.subsume_check(l, r)), - - _ if l.as_explicit_function().is_some() => { - let (arg_l, ret_l) = l.as_explicit_function().unwrap(); - let (arg_r, ret_r) = self.unify_function(r); - Ok(self.subsume_check_function(arg_l, ret_l, &arg_r, &ret_r)) - } - _ if r.as_explicit_function().is_some() => { - let (arg_r, ret_r) = r.as_explicit_function().unwrap(); - let (arg_l, ret_l) = self.unify_function(l); - Ok(self.subsume_check_function(&arg_l, &ret_l, arg_r, ret_r)) + (_, Type::Forall(params, r)) => { + let mut variables = params + .iter() + .map(|param| (param.id.clone(), subs.new_var())) + .collect(); + let r = r.instantiate_generics(&mut subs, &mut variables); + self.try_match_res(l, &r) } + + (Type::Forall(_, _), _) => Ok(self.subsume_check(l, r)), + _ => { // Both sides are concrete types, the only way they can be equal is if // the matcher finds their top level to be equal (and their sub-terms // unify) - l.zip_match(r, self) + match (l.as_explicit_function(), r.as_explicit_function()) { + (None, None) => l.zip_match(r, self), + (l_func, r_func) => { + let x; + let (arg_l, ret_l) = match l_func { + Some(x) => x, + None => { + x = self.unify_function(l); + (&x.0, &x.1) + } + }; + let y; + let (arg_r, ret_r) = match r_func { + Some(y) => y, + None => { + y = self.unify_function(r); + (&y.0, &y.1) + } + }; + Ok(self.subsume_check_function(arg_l, ret_l, &arg_r, &ret_r)) + } + } } } } From da861ebad00a6f336d7fc7cbc99bf8703cd4b567 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 11 Aug 2019 14:20:06 +0200 Subject: [PATCH 03/27] perf(check): Only add implicit fields if the binding is implicit When encountering a binding we no longer recurse through the entire record but instead only do it as long as we are in a implicit binding already. Thus adding `Applicative` also adds its `Functor` field since `Applicative` is implicit. However, for a normal record such as from `let int = import! std.int` we no longer recurse and add all the implicit instances in `std.int`, instead a `let int @ { ? }` is required. BREAKING CHANGE --- base/src/resolve.rs | 35 ++++++++++++++++++------- check/src/implicits.rs | 58 +++++++++++++++++++++++++++++------------- tests/vm.rs | 24 ----------------- 3 files changed, 66 insertions(+), 51 deletions(-) diff --git a/base/src/resolve.rs b/base/src/resolve.rs index 4585bd491f..8655e2a37d 100644 --- a/base/src/resolve.rs +++ b/base/src/resolve.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use crate::{ fnv::FnvMap, symbol::Symbol, - types::{AliasRef, Type, TypeContext, TypeEnv, TypeExt}, + types::{AliasData, AliasRef, Type, TypeContext, TypeEnv, TypeExt}, }; quick_error! { @@ -106,7 +106,7 @@ impl AliasRemover { T: TypeExt + ::std::fmt::Display, { loop { - typ = match self.remove_alias_to_concrete(env, interner, &typ)? { + typ = match self.remove_alias_to_concrete(env, interner, &typ, |_| true)? { Some((typ, args)) => match *typ { Type::Builtin(..) | Type::Function(..) @@ -134,16 +134,29 @@ impl AliasRemover { } pub fn remove_aliases( + &mut self, + env: &dyn TypeEnv, + interner: &mut impl TypeContext, + typ: T, + ) -> Result + where + T: TypeExt + ::std::fmt::Display, + { + self.remove_aliases_predicate(env, interner, typ, |_| true) + } + + pub fn remove_aliases_predicate( &mut self, env: &dyn TypeEnv, interner: &mut impl TypeContext, mut typ: T, + mut predicate: impl FnMut(&AliasData) -> bool, ) -> Result where T: TypeExt + ::std::fmt::Display, { loop { - typ = match self.remove_alias(env, interner, &typ)? { + typ = match self.remove_alias(env, interner, &typ, &mut predicate)? { Some(typ) => typ, None => return Ok(typ), }; @@ -155,19 +168,20 @@ impl AliasRemover { env: &dyn TypeEnv, interner: &mut impl TypeContext, typ: &T, + predicate: impl FnOnce(&AliasData) -> bool, ) -> Result, Error> where T: TypeExt + ::std::fmt::Display, { - Ok(self.remove_alias_to_concrete(env, interner, typ)?.map( - |(non_replaced_type, unapplied_args)| { + Ok(self + .remove_alias_to_concrete(env, interner, typ, predicate)? + .map(|(non_replaced_type, unapplied_args)| { let non_replaced_type = non_replaced_type .replace_generics(interner, &mut self.named_variables) .unwrap_or_else(|| non_replaced_type.clone()); interner.app(non_replaced_type, unapplied_args.iter().cloned().collect()) - }, - )) + })) } pub fn remove_alias_to_concrete<'a>( @@ -175,13 +189,16 @@ impl AliasRemover { env: &'a dyn TypeEnv, interner: &mut impl TypeContext, typ: &'a T, + predicate: impl FnOnce(&AliasData) -> bool, ) -> Result)>, Error> where T: TypeExt + ::std::fmt::Display, { match peek_alias(env, &typ)? { - Some(alias) => self.remove_alias_to_concrete_inner(interner, typ, alias), - None => Ok(None), + Some(alias) if predicate(alias) => { + self.remove_alias_to_concrete_inner(interner, typ, alias) + } + _ => Ok(None), } } diff --git a/check/src/implicits.rs b/check/src/implicits.rs index 8278aa1949..c1b6b0ea2a 100644 --- a/check/src/implicits.rs +++ b/check/src/implicits.rs @@ -776,7 +776,17 @@ impl<'a> ImplicitResolver<'a> { } pub fn on_stack_var(&mut self, subs: &Substitution, id: &Symbol, typ: &RcType) { - self.add_implicits_of_record(subs, id, typ); + self.alias_resolver.clear(); + + self.path.clear(); + self.path.push(TypedIdent { + name: id.clone(), + typ: typ.clone(), + }); + + let meta = self.metadata.get(id).cloned(); + + self.add_implicits_of_ident(subs, typ, meta.as_ref().map(|m| &**m), &mut Vec::new()); } pub fn add_implicits_of_record( @@ -797,7 +807,7 @@ impl<'a> ImplicitResolver<'a> { self.add_implicits_of_record_rec(subs, typ, meta.as_ref().map(|m| &**m), &mut Vec::new()); } - fn add_implicits_of_record_rec( + fn add_implicits_of_ident( &mut self, mut subs: &Substitution, typ: &RcType, @@ -821,30 +831,47 @@ impl<'a> ImplicitResolver<'a> { let opt = self.try_create_implicit(metadata, typ); - let mut typ = typ.clone(); if let Some(definition) = opt { let typ = subs.forall(forall_params.iter().cloned().collect(), typ.clone()); self.implicit_bindings .insert(subs, definition, &self.path, &typ); + + self.add_implicits_of_record_rec(subs, &typ, metadata, forall_params) } + } + fn add_implicits_of_record_rec( + &mut self, + mut subs: &Substitution, + typ: &RcType, + metadata: Option<&Metadata>, + forall_params: &mut Vec>, + ) { let forall_params_len_before = forall_params.len(); + let mut typ = typ.clone(); while let Type::Forall(params, next) = &*typ { forall_params.extend(params.iter().cloned()); typ = next.clone(); } - let raw_type = - match self - .alias_resolver - .remove_aliases(&self.environment, &mut subs, typ.clone()) - { - Ok(t) => t, - // Don't recurse into self recursive aliases - Err(_) => return, - }; + let t = self.alias_resolver.remove_aliases_predicate( + &self.environment, + &mut subs, + typ.clone(), + |alias| { + alias + .unresolved_type() + .flags() + .contains(Flags::HAS_IMPLICIT) + }, + ); + let raw_type = match t { + Ok(t) => t, + // Don't recurse into self recursive aliases + Err(_) => return, + }; match *raw_type { Type::Record(_) => { for field in raw_type.row_iter() { @@ -861,12 +888,7 @@ impl<'a> ImplicitResolver<'a> { typ: field.typ.clone(), }); - self.add_implicits_of_record_rec( - subs, - &field.typ, - field_metadata, - forall_params, - ); + self.add_implicits_of_ident(subs, &field.typ, field_metadata, forall_params); self.path.pop(); } diff --git a/tests/vm.rs b/tests/vm.rs index 016b46b21d..656e3462de 100644 --- a/tests/vm.rs +++ b/tests/vm.rs @@ -409,30 +409,6 @@ f 0 (\r -> { x = r #Int+ 1 }) 1i32 } -#[test] -fn overloaded_bindings() { - let _ = ::env_logger::try_init(); - let text = r#" -#[implicit] -let add_int x y = x #Int+ y -#[implicit] -let add_float x y = x #Float+ y - -let add ?f: [a -> a -> a] -> a -> a -> a = f - -{ x = add 1 2, y = add 1.0 2.0 } -"#; - let vm = make_vm(); - let result = run_expr::>(&vm, text); - match result.get_ref() { - ValueRef::Data(data) => { - assert_eq!(data.get(0).unwrap(), ValueRef::Int(3)); - assert_eq!(data.get(1).unwrap(), ValueRef::Float(3.0)); - } - _ => panic!(), - } -} - test_expr! { record_base_duplicate_fields, r#" { x = "" .. { x = 1 } }.x From a7ac9f800d7f1ad944d81140dfe6be99d59f7943 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sun, 11 Aug 2019 21:51:44 +0200 Subject: [PATCH 04/27] perf: Avoid computing the plain name in name_eq (-3%) --- base/src/symbol.rs | 200 +++++++++++++++++++----------- base/tests/types.rs | 2 +- check/src/lib.rs | 2 +- check/src/rename.rs | 25 ++-- check/src/typecheck.rs | 11 +- check/src/typecheck/generalize.rs | 4 +- check/tests/support/mod.rs | 8 +- completion/tests/support/mod.rs | 6 +- src/compiler_pipeline.rs | 8 +- vm/src/api/typ.rs | 38 +++--- vm/src/compiler.rs | 2 +- vm/src/core/grammar.lalrpop | 6 +- vm/src/derive/deserialize.rs | 21 ++-- vm/src/derive/eq.rs | 24 ++-- vm/src/derive/mod.rs | 16 +-- vm/src/derive/serialize.rs | 16 +-- vm/src/derive/show.rs | 41 +++--- vm/src/serialization.rs | 2 +- vm/src/vm.rs | 5 +- 19 files changed, 267 insertions(+), 170 deletions(-) diff --git a/base/src/symbol.rs b/base/src/symbol.rs index bf5ebf5902..985ebbe6b5 100644 --- a/base/src/symbol.rs +++ b/base/src/symbol.rs @@ -13,7 +13,14 @@ use crate::fnv::FnvMap; /// A symbol uniquely identifies something regardless of its name and which module it originated /// from #[derive(Clone, Eq)] -pub struct Symbol(Arc); +pub struct Symbol(Arc); + +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct SymbolData { + pub global: bool, + pub location: Option<(u32, u32)>, + pub name: N, +} #[cfg(feature = "serde")] mod serialization { @@ -30,7 +37,7 @@ mod serialization { D: Deserializer<'de>, { use std::borrow::Cow; - Cow::::deserialize(deserializer).map(Symbol::from) + Cow::::deserialize(deserializer).map(|s| Symbol::from(&s[..])) } } @@ -46,7 +53,8 @@ mod serialization { use crate::serialization::SharedSeed; let seed = SharedSeed::new(seed); - seed.deserialize(deserializer).map(Symbol) + seed.deserialize(deserializer) + .map(|s: String| Symbol::from(&s[..])) } } @@ -55,8 +63,7 @@ mod serialization { where S: Serializer, { - let s: &str = self.as_ref(); - s.serialize(serializer) + serializer.collect_str(self) } } @@ -75,7 +82,7 @@ mod serialization { impl Deref for Symbol { type Target = SymbolRef; fn deref(&self) -> &SymbolRef { - unsafe { &*(&*(self.0).0 as *const str as *const SymbolRef) } + unsafe { &*(&*self.0.name.0 as *const str as *const SymbolRef) } } } @@ -87,7 +94,7 @@ impl Borrow for Symbol { impl AsRef for Symbol { fn as_ref(&self) -> &str { - self.0.as_pretty_str() + self.0.name.as_str() } } @@ -99,7 +106,19 @@ impl fmt::Debug for Symbol { impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", &**self) + write!( + f, + "{}{}", + if self.is_global() { "@" } else { "" }, + self.0.name + )?; + if let Some((x, y)) = self.0.location { + write!(f, ":{}", x)?; + if y != 0 { + write!(f, "_{}", y)?; + } + } + Ok(()) } } @@ -139,12 +158,47 @@ impl Hash for Symbol { } } -impl From for Symbol +impl From for Symbol { + fn from(name: String) -> Symbol { + Symbol::from(&*name) + } +} + +impl From<&'_ str> for Symbol { + fn from(name: &str) -> Symbol { + Symbol(Arc::new(SymbolData::from(name))) + } +} + +impl<'a, N> From<&'a str> for SymbolData where - S: Into, + N: From<&'a str>, { - fn from(name: S) -> Symbol { - Symbol(Arc::new(NameBuf(name.into()))) + fn from(mut name: &'a str) -> SymbolData { + let global = name.starts_with('@'); + let location = match name + .bytes() + .rposition(|b| (b < b'0' || b > b'9') && b != b'_') + { + Some(i) if name.as_bytes()[i] == b':' => { + let opt = name[(i + 1)..].parse::().ok().map(|x| (x, 0)); + + name = &name[..i]; + + opt + } + _ => None, + }; + + if global { + name = &name[1..]; + } + + SymbolData { + global, + location, + name: name.into(), + } } } @@ -196,6 +250,10 @@ impl Symbol { pub fn strong_count(sym: &Symbol) -> usize { Arc::strong_count(&sym.0) } + + pub fn is_global(&self) -> bool { + self.0.global + } } impl SymbolRef { @@ -210,7 +268,7 @@ impl SymbolRef { } pub fn as_pretty_str(&self) -> &str { - Name::new(&self.0).as_pretty_str() + Name::new(&self.0).as_str() } pub fn as_str(&self) -> &str { @@ -218,7 +276,7 @@ impl SymbolRef { } pub fn name(&self) -> &Name { - Name::new(Name::new(&self.0).as_pretty_str()) + Name::new(&self.0) } pub fn raw_name(&self) -> &Name { @@ -235,10 +293,6 @@ impl SymbolRef { Name::new(&self.0).definition_name() } - pub fn is_global(&self) -> bool { - self.0.starts_with('@') - } - fn ptr(&self) -> *const () { self.0.as_bytes().as_ptr() as *const () } @@ -292,22 +346,19 @@ impl<'a> Iterator for Components<'a> { } } +impl<'a> From<&'a str> for &'a Name { + fn from(s: &'a str) -> &'a Name { + Name::new(s) + } +} + impl Name { #[inline] pub fn new>(n: &N) -> &Name { unsafe { &*(n.as_ref() as *const str as *const Name) } } - pub fn as_pretty_str(&self) -> &str { - Self::strip_position_suffix(&self.0) - } - pub fn as_str(&self) -> &str { - debug_assert!( - !self.0.contains(':'), - "Did you mean to call as_pretty_str?: {}", - &self.0 - ); &self.0 } @@ -332,24 +383,7 @@ impl Name { } pub fn definition_name(&self) -> &str { - Self::strip_position_suffix(if self.0.as_bytes().get(0) == Some(&b'@') { - &self.0[1..] - } else { - &self.0 - }) - } - - fn strip_position_suffix(name: &str) -> &str { - // Strip away a `:1234_56` suffix - let x = match name - .bytes() - .rposition(|b| (b < b'0' || b > b'9') && b != b'_') - { - Some(i) if name.as_bytes()[i] == b':' => &name[..i], - _ => name, - }; - - x + self.as_str() } } @@ -445,7 +479,7 @@ impl<'a> From<&'a Name> for NameBuf { /// Used to make identifiers within a single module point to the same symbol #[derive(Debug, Default)] pub struct Symbols { - indexes: FnvMap<&'static Name, Symbol>, + indexes: FnvMap, Symbol>, } impl Symbols { @@ -455,30 +489,58 @@ impl Symbols { } } - fn make_symbol(&mut self, name: NameBuf) -> Symbol { + fn make_symbol(&mut self, global: bool, location: Option<(u32, u32)>, name: NameBuf) -> Symbol { // `name` is fixed in memory and the key lives as long as `s` this is safe let key = unsafe { &*(&*name as *const Name) }; - let s = Symbol(Arc::new(name)); - self.indexes.insert(key, s.clone()); + let s = Symbol(Arc::new(SymbolData { + global, + location, + name, + })); + self.indexes.insert( + SymbolData { + global, + location, + name: key, + }, + s.clone(), + ); s } + pub fn simple_symbol(&mut self, name: N) -> Symbol + where + N: Into + AsRef, + { + self.symbol(SymbolData { + global: false, + 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: N) -> Symbol + pub fn symbol(&mut self, name: SymbolData) -> Symbol where N: Into + AsRef, { - if let Some(symbol) = self.indexes.get(name.as_ref()) { + let name_ref = SymbolData { + global: name.global, + location: name.location, + name: name.name.as_ref(), + }; + if let Some(symbol) = self.indexes.get(&name_ref) { return symbol.clone(); } - self.make_symbol(name.into()) + self.make_symbol(name.global, name.location, name.name.into()) } pub fn contains_name(&mut self, name: N) -> bool where N: AsRef, { - self.indexes.contains_key(name.as_ref()) + let s = SymbolData::<&Name>::from(name.as_ref().as_str()); + self.indexes.contains_key(&s) } pub fn len(&self) -> usize { @@ -508,8 +570,15 @@ impl<'a> SymbolModule<'a> { } } + pub fn simple_symbol(&mut self, name: N) -> Symbol + where + N: Into + AsRef, + { + self.symbols.simple_symbol(name) + } + /// Creates an unprefixed symbol, same as `Symbols::symbol` - pub fn symbol(&mut self, name: N) -> Symbol + pub fn symbol(&mut self, name: SymbolData) -> Symbol where N: Into + AsRef, { @@ -529,14 +598,18 @@ impl<'a> SymbolModule<'a> { /// # use gluon_base::symbol::{Symbols, SymbolModule}; /// let mut symbols = Symbols::new(); /// let mut symbols = SymbolModule::new(String::from("test"), &mut symbols); - /// assert_eq!(symbols.symbol("a").as_ref(), "a"); + /// assert_eq!(symbols.simple_symbol("a").as_ref(), "a"); /// assert_eq!(symbols.scoped_symbol("a").as_ref(), "test.a"); /// ``` pub fn scoped_symbol(&mut self, name: &str) -> Symbol { let len = self.module.0.len(); self.module.0.push('.'); self.module.0.push_str(name); - let symbol = self.symbols.symbol(&*self.module); + let symbol = self.symbols.symbol(SymbolData { + global: false, + location: None, + name: &*self.module, + }); self.module.0.truncate(len); symbol } @@ -568,7 +641,7 @@ impl DisplayEnv for Symbols { impl IdentEnv for Symbols { fn from_str(&mut self, s: &str) -> Symbol { - self.symbol(s) + self.symbol(SymbolData::<&Name>::from(s)) } } @@ -582,17 +655,6 @@ impl<'s> DisplayEnv for SymbolModule<'s> { impl<'a> IdentEnv for SymbolModule<'a> { fn from_str(&mut self, s: &str) -> Symbol { - self.symbol(s) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn definition_name() { - assert_eq!(Name::new("a:123").definition_name(), "a"); - assert_eq!(Name::new("a:123_5").definition_name(), "a"); + self.symbol(SymbolData::<&Name>::from(s)) } } diff --git a/base/tests/types.rs b/base/tests/types.rs index cd99c29fad..b8fa721b7f 100644 --- a/base/tests/types.rs +++ b/base/tests/types.rs @@ -366,7 +366,7 @@ pub fn get_local_interner() -> Rc> { } pub fn intern(s: &str) -> Symbol { - get_local_interner().borrow_mut().symbol(s) + get_local_interner().borrow_mut().simple_symbol(s) } pub fn no_loc(value: T) -> Spanned { diff --git a/check/src/lib.rs b/check/src/lib.rs index 927b98b491..b47c65f813 100644 --- a/check/src/lib.rs +++ b/check/src/lib.rs @@ -113,7 +113,7 @@ mod tests { let mut interner = interner.borrow_mut(); if s.starts_with(char::is_lowercase) { - interner.symbol(s) + interner.simple_symbol(s) } else { SymbolModule::new("test".into(), &mut interner).scoped_symbol(s) } diff --git a/check/src/rename.rs b/check/src/rename.rs index 66f3eb3662..9f45e97800 100644 --- a/check/src/rename.rs +++ b/check/src/rename.rs @@ -7,7 +7,7 @@ use crate::base::{ pos::{self, ByteOffset, BytePos, Span}, scoped_map::ScopedMap, source::Source, - symbol::{Symbol, SymbolModule}, + symbol::{Symbol, SymbolData, SymbolModule}, types::Type, }; @@ -82,7 +82,7 @@ pub fn rename<'s>( fn stack_var(&mut self, id: Symbol, span: Span) -> Symbol { let new_id = self.symbols - .symbol(format!("{}:{}", self.symbols.string(&id), span.start())); + .symbol(SymbolData { global: false, name: id.as_str(), location: Some((span.start().0, 0)) }); let index = self.seen_symbols.entry(new_id.clone()).or_default(); let new_id = if *index == 0 { @@ -90,12 +90,11 @@ pub fn rename<'s>( new_id } else { *index += 1; - self.symbols.symbol(format!( - "{}:{}_{}", - self.symbols.string(&id), - span.start(), - index - )) + self.symbols.symbol(SymbolData { + global: false, + name: id.as_str(), + location: Some((span.start().0, *index)), + } ) }; debug!("Rename binding `{:?}` = `{:?}`", id, new_id); @@ -212,8 +211,12 @@ pub fn rename<'s>( } Expr::Lambda(ref mut lambda) => { let location = self.source.location(expr.span.start()).unwrap_or_else(|| ice!("Lambda without source location")); - let name = format!("{}.lambda:{}_{}", self.symbols.module(), location.line.number(), location.column.number()); - lambda.id.name = self.symbols.symbol(name); + let name = format!("{}.lambda", self.symbols.module()); + lambda.id.name = self.symbols.symbol(SymbolData { global: false, location: + + + Some(( location.line.0 + 1, location.column.0 + 1) + ),name }); self.env.stack.enter_scope(); @@ -243,7 +246,7 @@ pub fn rename<'s>( ref mut flat_map_id, .. }) => { - let flat_map = self.symbols.symbol("flat_map"); + let flat_map = self.symbols.simple_symbol("flat_map"); *flat_map_id = Some(Box::new(pos::spanned( Span::new(expr.span.end(), expr.span.start() + ByteOffset::from(2)), Expr::Ident(TypedIdent { diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index 993bbb3ffc..8e295cbb90 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -336,7 +336,7 @@ impl<'a> Typecheck<'a> { ctor_type, ); - let symbol = self.symbols.symbols().symbol(field.name.as_str()); + let symbol = self.symbols.symbols().simple_symbol(field.name.as_str()); self.stack_var(symbol, typ); } } @@ -837,7 +837,7 @@ impl<'a> Typecheck<'a> { let typ = self.infer_expr(expr); modifier |= typ.modifier; Field { - name: self.symbols.symbol(format!("_{}", i)), + name: self.symbols.simple_symbol(format!("_{}", i)), typ: typ.concrete, } }) @@ -1496,7 +1496,7 @@ impl<'a> Typecheck<'a> { &mut args[i - 1].name.value } _ => { - let id = Symbol::from(format!("__implicit_arg")); + let id = Symbol::from("__implicit_arg"); let pos = if i == 0 { before_args_pos } else { @@ -2249,7 +2249,7 @@ impl<'a> Typecheck<'a> { } // HACK // For type projections - let id = self.symbols.symbol(id.declared_name()); + let id = self.symbols.simple_symbol(id.declared_name()); if let Some(bind) = self.environment.stack.get_mut(&id) { bind.typ.concrete = typ.clone(); } @@ -2974,7 +2974,8 @@ impl<'a> Typecheck<'a> { self.errors.push(Spanned { span: span, // TODO Help to the other fields location - value: TypeError::DuplicateField(self.symbols.symbols().symbol(name)).into(), + value: TypeError::DuplicateField(self.symbols.symbols().simple_symbol(name)) + .into(), }); false }) diff --git a/check/src/typecheck/generalize.rs b/check/src/typecheck/generalize.rs index 193d7634e8..18284dff24 100644 --- a/check/src/typecheck/generalize.rs +++ b/check/src/typecheck/generalize.rs @@ -260,7 +260,7 @@ impl TypeVariableGenerator { } else { let name = format!("{}{}", self.name, self.i); self.i += 1; - tc.symbols.symbol(&name[..]) + tc.symbols.simple_symbol(&name[..]) }; self.map.insert(symbol.clone()); let hole = tc.hole(); @@ -271,7 +271,7 @@ impl TypeVariableGenerator { fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol { for c in b'a'..(b'z' + 1) { self.name.push(c as char); - let symbol = tc.symbols.symbol(&self.name[..]); + let symbol = tc.symbols.simple_symbol(&self.name[..]); if !self.map.contains(&symbol) && tc.environment.skolem_variables.get(&symbol).is_none() { return symbol; diff --git a/check/tests/support/mod.rs b/check/tests/support/mod.rs index dadfced87f..09c8c0679b 100644 --- a/check/tests/support/mod.rs +++ b/check/tests/support/mod.rs @@ -13,7 +13,7 @@ use self::{ kind::{ArcKind, Kind, KindEnv}, metadata::{Metadata, MetadataEnv}, pos::{BytePos, Spanned}, - symbol::{Symbol, SymbolModule, SymbolRef, Symbols}, + symbol::{Name, Symbol, SymbolData, SymbolModule, SymbolRef, Symbols}, types::{self, Alias, ArcType, Field, Generic, PrimitiveEnv, Type, TypeCache, TypeEnv}, }, check::{ @@ -63,7 +63,7 @@ pub fn intern_unscoped(s: &str) -> Symbol { let i = get_local_interner(); let mut i = i.borrow_mut(); - i.symbol(s) + i.simple_symbol(s) } pub fn intern(s: &str) -> Symbol { @@ -71,7 +71,7 @@ pub fn intern(s: &str) -> Symbol { let mut interner = interner.borrow_mut(); if s.starts_with(char::is_lowercase) { - interner.symbol(s) + interner.symbol(SymbolData::<&Name>::from(s)) } else { SymbolModule::new("test".into(), &mut interner).scoped_symbol(s) } @@ -103,7 +103,7 @@ impl MockEnv { MockEnv { bool: { - let bool_sym = interner.symbol("Bool"); + let bool_sym = interner.simple_symbol("Bool"); let bool_ty = Type::app(Type::ident(bool_sym.clone()), collect![]); Alias::new(bool_sym, Vec::new(), bool_ty) }, diff --git a/completion/tests/support/mod.rs b/completion/tests/support/mod.rs index add755525a..ceff000325 100644 --- a/completion/tests/support/mod.rs +++ b/completion/tests/support/mod.rs @@ -8,7 +8,7 @@ use crate::base::{ kind::{ArcKind, Kind, KindEnv}, metadata::{Metadata, MetadataEnv}, pos::BytePos, - symbol::{Symbol, SymbolModule, SymbolRef, Symbols}, + symbol::{Name, Symbol, SymbolData, SymbolModule, SymbolRef, Symbols}, types::{self, Alias, ArcType, Generic, PrimitiveEnv, Type, TypeCache, TypeEnv}, }; @@ -40,7 +40,7 @@ pub fn intern(s: &str) -> Symbol { let mut interner = interner.borrow_mut(); if s.starts_with(char::is_lowercase) { - interner.symbol(s) + interner.symbol(SymbolData::<&Name>::from(s)) } else { SymbolModule::new("test".into(), &mut interner).scoped_symbol(s) } @@ -65,7 +65,7 @@ impl MockEnv { let interner = get_local_interner(); let mut interner = interner.borrow_mut(); - let bool_sym = interner.symbol("Bool"); + let bool_sym = interner.simple_symbol("Bool"); let bool_ty = Type::app(Type::ident(bool_sym.clone()), collect![]); MockEnv { diff --git a/src/compiler_pipeline.rs b/src/compiler_pipeline.rs index 6dd9f0710f..5e57b87f00 100644 --- a/src/compiler_pipeline.rs +++ b/src/compiler_pipeline.rs @@ -23,7 +23,7 @@ use crate::base::fnv::FnvMap; use crate::base::metadata::Metadata; use crate::base::resolve; use crate::base::source::Source; -use crate::base::symbol::{Name, NameBuf, Symbol, SymbolModule}; +use crate::base::symbol::{Name, NameBuf, Symbol, SymbolData, SymbolModule}; use crate::base::types::{ArcType, NullInterner, Type}; use crate::check::{metadata, rename}; @@ -915,7 +915,11 @@ where } = try_future!(DeSeed::new(&vm, &mut vm.current_context()) .deserialize(self.0) .map_err(|err| err.to_string())); - let id = compiler.symbols.symbol(format!("@{}", name)); + let id = compiler.symbols.symbol(SymbolData { + global: true, + location: None, + name: name, + }); try_future!(vm.set_global( id, typ, diff --git a/vm/src/api/typ.rs b/vm/src/api/typ.rs index d672711939..daa06185d2 100644 --- a/vm/src/api/typ.rs +++ b/vm/src/api/typ.rs @@ -85,7 +85,10 @@ where } else { type_cache.variant(variants) }; - Ok((deserializer.state.symbols.symbol(deserializer.name), typ)) + Ok(( + deserializer.state.symbols.simple_symbol(deserializer.name), + typ, + )) } struct State<'de> { @@ -479,7 +482,7 @@ where Some(field) => { let value = seed.deserialize(&mut *self.deserializer)?; self.types.push(Field::new( - self.deserializer.state.symbols.symbol(field), + self.deserializer.state.symbols.simple_symbol(field), self.deserializer.typ.take().expect("typ"), )); Ok(value) @@ -518,7 +521,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { fn unit_variant(self) -> Result<()> { self.de.variant = Some(Field::ctor( - self.de.state.symbols.symbol(self.variant), + self.de.state.symbols.simple_symbol(self.variant), vec![], )); Ok(()) @@ -530,7 +533,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { { let value = seed.deserialize(&mut *self.de)?; self.de.variant = Some(Field::ctor( - self.de.state.symbols.symbol(self.variant), + self.de.state.symbols.simple_symbol(self.variant), vec![self.de.typ.take().expect("typ")], )); Ok(value) @@ -548,7 +551,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { ) }; self.de.variant = Some(Field::ctor( - self.de.state.symbols.symbol(self.variant), + self.de.state.symbols.simple_symbol(self.variant), types, )); Ok(value) @@ -566,7 +569,7 @@ impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { ) }; self.de.variant = Some(Field::ctor( - self.de.state.symbols.symbol(self.variant), + self.de.state.symbols.simple_symbol(self.variant), vec![self.de.state.cache.record(vec![], types)], )); Ok(value) @@ -597,8 +600,8 @@ mod tests { Type::record( vec![], vec![ - Field::new(symbols.symbol("x"), Type::int()), - Field::new(symbols.symbol("name"), Type::string()), + Field::new(symbols.simple_symbol("x"), Type::int()), + Field::new(symbols.simple_symbol("name"), Type::string()), ] ) ); @@ -622,12 +625,19 @@ mod tests { assert_eq!( typ, Type::variant(vec![ - Field::ctor(symbols.symbol("A"), vec![]), - Field::ctor(symbols.symbol("B"), vec![Type::int()]), - Field::ctor(symbols.symbol("C"), vec![Type::string(), Type::float()],), - Field::ctor(symbols.symbol("D"), vec![ - Type::record(vec![], vec![Field::new(symbols.symbol("foo"), Type::int())]) - ]), + Field::ctor(symbols.simple_symbol("A"), vec![]), + Field::ctor(symbols.simple_symbol("B"), vec![Type::int()]), + Field::ctor( + symbols.simple_symbol("C"), + vec![Type::string(), Type::float()], + ), + Field::ctor( + symbols.simple_symbol("D"), + vec![Type::record( + vec![], + vec![Field::new(symbols.simple_symbol("foo"), Type::int())] + )] + ), ]) ); } diff --git a/vm/src/compiler.rs b/vm/src/compiler.rs index d932f54412..e1307981f3 100644 --- a/vm/src/compiler.rs +++ b/vm/src/compiler.rs @@ -491,7 +491,7 @@ impl<'a> Compiler<'a> { Compiler { globals: globals, vm: vm, - empty_symbol: symbols.symbol(""), + empty_symbol: symbols.simple_symbol(""), symbols: symbols, stack_types: ScopedMap::new(), source: source, diff --git a/vm/src/core/grammar.lalrpop b/vm/src/core/grammar.lalrpop index a58beda0c2..2648c72d1d 100644 --- a/vm/src/core/grammar.lalrpop +++ b/vm/src/core/grammar.lalrpop @@ -17,8 +17,8 @@ Comma: Vec = Identifier: Symbol = { - => symbols.symbol(<>), - => symbols.symbol(&<>[1..<>.len() - 1]) + => symbols.simple_symbol(<>), + => symbols.simple_symbol(&<>[1..<>.len() - 1]) }; Field: (Symbol, Option) = { @@ -61,7 +61,7 @@ AtomicExpr: Expr<'a> = { "(" ")", "{" > "}" => { let id = TypedIdent { - name: symbols.symbol(""), + name: symbols.simple_symbol(""), typ: Type::record(vec![], args.iter() .map(|&(ref arg, _)| Field { name: arg.clone(), typ: Type::hole(), }) .collect()), diff --git a/vm/src/derive/deserialize.rs b/vm/src/derive/deserialize.rs index b2863fec90..ea525710f7 100644 --- a/vm/src/derive/deserialize.rs +++ b/vm/src/derive/deserialize.rs @@ -15,10 +15,10 @@ pub fn generate( ) -> Result, Error> { let span = bind.name.span; - let deserializer_fn = TypedIdent::new(symbols.symbol("deserializer")); + let deserializer_fn = TypedIdent::new(symbols.simple_symbol("deserializer")); - let field_deserialize = symbols.symbol("field"); - let deserializer_ident = ident(span, symbols.symbol("deserializer")); + let field_deserialize = symbols.simple_symbol("field"); + let deserializer_ident = ident(span, symbols.simple_symbol("deserializer")); let self_type: AstType<_> = Type::app( Type::ident(bind.alias.value.name.clone()), @@ -75,14 +75,19 @@ pub fn generate( .fold(None, |acc, variant| { let deserialize_variant = app( span, - symbols.symbol("map"), + symbols.simple_symbol("map"), vec![ ident(span, variant.name.clone()), deserializer_ident.clone(), ], ); Some(match acc { - Some(prev) => infix(span, prev, symbols.symbol("<|>"), deserialize_variant), + Some(prev) => infix( + span, + prev, + symbols.simple_symbol("<|>"), + deserialize_variant, + ), None => deserialize_variant, }) }) @@ -108,7 +113,7 @@ pub fn generate( expr: deserializer_expr, metadata: Default::default(), typ: Some(Type::app( - Type::ident(symbols.symbol("ValueDeserializer")), + Type::ident(symbols.simple_symbol("ValueDeserializer")), collect![self_type.clone()], )), resolved_type: Type::hole(), @@ -121,7 +126,7 @@ pub fn generate( types: Vec::new(), exprs: vec![ExprField { metadata: Default::default(), - name: pos::spanned(span, symbols.symbol("deserializer")), + name: pos::spanned(span, symbols.simple_symbol("deserializer")), value: Some(ident(span, deserializer_fn.name.clone())), }], base: None, @@ -144,7 +149,7 @@ pub fn generate( Ok(ValueBinding { name: pos::spanned( span, - Pattern::Ident(TypedIdent::new(symbols.symbol(format!( + Pattern::Ident(TypedIdent::new(symbols.simple_symbol(format!( "deserialize_{}", bind.alias.value.name.declared_name() )))), diff --git a/vm/src/derive/eq.rs b/vm/src/derive/eq.rs index 27e8e640e3..fd8508cd08 100644 --- a/vm/src/derive/eq.rs +++ b/vm/src/derive/eq.rs @@ -18,7 +18,7 @@ pub fn generate( ) -> Result, Error> { let span = bind.name.span; - let eq = TypedIdent::new(symbols.symbol("eq")); + let eq = TypedIdent::new(symbols.simple_symbol("eq")); let l = Symbol::from("l"); let r = Symbol::from("r"); @@ -38,7 +38,7 @@ pub fn generate( let equal_symbol = if self_type { eq.name.clone() } else { - symbols.symbol("==") + symbols.simple_symbol("==") }; let eq_check = app( @@ -51,18 +51,21 @@ pub fn generate( ); Some(match acc { - Some(acc) => infix(span, acc, symbols.symbol("&&"), eq_check), + Some(acc) => infix(span, acc, symbols.simple_symbol("&&"), eq_check), None => eq_check, }) }) - .unwrap_or_else(|| ident(span, symbols.symbol("True"))) + .unwrap_or_else(|| ident(span, symbols.simple_symbol("True"))) }; let comparison_expr = match **remove_forall(bind.alias.value.unresolved_type()) { Type::Variant(ref variants) => { let catch_all_alternative = Alternative { - pattern: pos::spanned(span, Pattern::Ident(TypedIdent::new(symbols.symbol("_")))), - expr: ident(span, symbols.symbol("False")), + pattern: pos::spanned( + span, + Pattern::Ident(TypedIdent::new(symbols.simple_symbol("_"))), + ), + expr: ident(span, symbols.simple_symbol("False")), }; let alts = row_iter(variants) @@ -202,7 +205,7 @@ pub fn generate( types: Vec::new(), exprs: vec![ExprField { metadata: Default::default(), - name: pos::spanned(span, symbols.symbol("==")), + name: pos::spanned(span, symbols.simple_symbol("==")), value: Some(ident(span, eq.name.clone())), }], base: None, @@ -213,9 +216,10 @@ pub fn generate( Ok(ValueBinding { name: pos::spanned( span, - Pattern::Ident(TypedIdent::new( - symbols.symbol(format!("eq_{}", bind.alias.value.name.declared_name())), - )), + Pattern::Ident(TypedIdent::new(symbols.simple_symbol(format!( + "eq_{}", + bind.alias.value.name.declared_name() + )))), ), args: Vec::new(), expr: pos::spanned(span, eq_record_expr), diff --git a/vm/src/derive/mod.rs b/vm/src/derive/mod.rs index 8bf5b882bf..58d41457e0 100644 --- a/vm/src/derive/mod.rs +++ b/vm/src/derive/mod.rs @@ -53,12 +53,12 @@ fn sequence_actions( .map(|id| Argument::explicit(pos::spanned(span, id))) .collect(), body: Box::new(packed_expr), - id: TypedIdent::new(symbols.symbol("pack_record")), + id: TypedIdent::new(symbols.simple_symbol("pack_record")), }), ); let map_expr = app( span, - symbols.symbol("map"), + symbols.simple_symbol("map"), vec![ paren(span, pack_record), paren(span, action(&field_symbols.first().expect("FIXME").name)), @@ -67,7 +67,7 @@ fn sequence_actions( field_symbols.iter().skip(1).fold(map_expr, |prev, symbol| { let deserialize_field = action(&symbol.name); - infix(span, prev, symbols.symbol("<*>"), deserialize_field) + infix(span, prev, symbols.simple_symbol("<*>"), deserialize_field) }) } @@ -127,14 +127,14 @@ fn generate_import_( types: type_fields .iter() .map(|f| PatternField { - name: pos::spanned(span, symbols.symbol(*f)), + name: pos::spanned(span, symbols.simple_symbol(*f)), value: None, }) .collect(), fields: fields .iter() .map(|f| PatternField { - name: pos::spanned(span, symbols.symbol(*f)), + name: pos::spanned(span, symbols.simple_symbol(*f)), value: None, }) .collect(), @@ -143,7 +143,7 @@ fn generate_import_( args: Vec::new(), expr: app( span, - symbols.symbol("import!"), + symbols.simple_symbol("import!"), vec![project(span, symbols, import)], ), metadata: Default::default(), @@ -155,7 +155,7 @@ fn generate_import_( fn project(span: Span, symbols: &mut Symbols, p: &str) -> SpannedExpr { p.split('.') .fold(None, |acc, name| { - let symbol = symbols.symbol(name); + let symbol = symbols.simple_symbol(name); Some(match acc { Some(expr) => { pos::spanned(span, Expr::Projection(Box::new(expr), symbol, Type::hole())) @@ -226,7 +226,7 @@ fn binding_type( self_type: AstType, bind: &TypeBinding, ) -> AstType { - let derive_type: AstType<_> = Type::ident(symbols.symbol(derive_type_name)); + let derive_type: AstType<_> = Type::ident(symbols.simple_symbol(derive_type_name)); Type::function_implicit( bind.alias .value diff --git a/vm/src/derive/serialize.rs b/vm/src/derive/serialize.rs index e50fc4fbc3..a6c26d4db8 100644 --- a/vm/src/derive/serialize.rs +++ b/vm/src/derive/serialize.rs @@ -20,7 +20,7 @@ pub fn generate( let x = Symbol::from("x"); - let serialize_fn = TypedIdent::new(symbols.symbol("serialize")); + let serialize_fn = TypedIdent::new(symbols.simple_symbol("serialize")); let self_type: AstType<_> = Type::app( Type::ident(bind.alias.value.name.clone()), @@ -48,20 +48,20 @@ pub fn generate( let map = app( span, - symbols.symbol("singleton"), + symbols.simple_symbol("singleton"), vec![literal(span, symbol.name.declared_name()), serialize_field], ); Some(match prev { - Some(prev) => infix(span, prev, symbols.symbol("<>"), map), + Some(prev) => infix(span, prev, symbols.simple_symbol("<>"), map), None => map, }) }) - .unwrap_or_else(|| ident(span, symbols.symbol("empty"))); + .unwrap_or_else(|| ident(span, symbols.simple_symbol("empty"))); let construct_object_expr = app( span, - symbols.symbol("Object"), + symbols.simple_symbol("Object"), vec![paren(span, construct_map_expr)], ); @@ -167,7 +167,7 @@ pub fn generate( let semigroup_import = generate_import(span, symbols, &[], &["<>"], "std.semigroup"); let result_import = generate_import_(span, symbols, &[], &[], true, "std.result"); - let serialize_ = TypedIdent::new(symbols.symbol("serialize_")); + let serialize_ = TypedIdent::new(symbols.simple_symbol("serialize_")); let serializer_binding = ValueBinding { name: pos::spanned(span, Pattern::Ident(serialize_.clone())), args: vec![Argument::explicit(pos::spanned( @@ -187,7 +187,7 @@ pub fn generate( types: Vec::new(), exprs: vec![ExprField { metadata: Default::default(), - name: pos::spanned(span, symbols.symbol("serialize")), + name: pos::spanned(span, symbols.simple_symbol("serialize")), value: Some(ident(span, serialize_.name.clone())), }], base: None, @@ -212,7 +212,7 @@ pub fn generate( Ok(ValueBinding { name: pos::spanned( span, - Pattern::Ident(TypedIdent::new(symbols.symbol(format!( + Pattern::Ident(TypedIdent::new(symbols.simple_symbol(format!( "serialize_{}", bind.alias.value.name.declared_name() )))), diff --git a/vm/src/derive/show.rs b/vm/src/derive/show.rs index bbf365110e..bd0f32c601 100644 --- a/vm/src/derive/show.rs +++ b/vm/src/derive/show.rs @@ -16,7 +16,7 @@ pub fn generate( let span = bind.name.span; let x = Symbol::from("x"); - let show_fn = TypedIdent::new(symbols.symbol("show_")); + let show_fn = TypedIdent::new(symbols.simple_symbol("show_")); let show_expr = match **remove_forall(bind.alias.value.unresolved_type()) { Type::Variant(ref variants) => { @@ -41,24 +41,29 @@ pub fn generate( let show_function = if self_type { show_fn.name.clone() } else { - symbols.symbol("show") + symbols.simple_symbol("show") }; let show = infix( span, literal(span, "("), - symbols.symbol("++"), + symbols.simple_symbol("++"), infix( span, app(span, show_function, vec![ident(span, x.name.clone())]), - symbols.symbol("++"), + symbols.simple_symbol("++"), literal(span, ")"), ), ); infix( span, acc, - symbols.symbol("++"), - infix(span, literal(span, " "), symbols.symbol("++"), show), + symbols.simple_symbol("++"), + infix( + span, + literal(span, " "), + symbols.simple_symbol("++"), + show, + ), ) }) }; @@ -99,18 +104,18 @@ pub fn generate( .fold(open_brace, |acc, (i, x)| { let show = app( span, - symbols.symbol("show"), + symbols.simple_symbol("show"), vec![ident(span, x.name.clone())], ); let show_field = infix( span, acc, - symbols.symbol("++"), + symbols.simple_symbol("++"), infix( span, literal(span, &format!("{} = ", x.name.declared_name())), - symbols.symbol("++"), + symbols.simple_symbol("++"), show, ), ); @@ -120,12 +125,17 @@ pub fn generate( infix( span, show_field, - symbols.symbol("++"), + symbols.simple_symbol("++"), literal(span, suffix), ) }); - infix(span, show_expr, symbols.symbol("++"), literal(span, "}")) + infix( + span, + show_expr, + symbols.simple_symbol("++"), + literal(span, "}"), + ) }; Expr::Match( Box::new(ident(span, x.clone())), @@ -168,7 +178,7 @@ pub fn generate( types: Vec::new(), exprs: vec![ExprField { metadata: Default::default(), - name: pos::spanned(span, symbols.symbol("show")), + name: pos::spanned(span, symbols.simple_symbol("show")), value: Some(ident(span, show_fn.name.clone())), }], base: None, @@ -179,9 +189,10 @@ pub fn generate( Ok(ValueBinding { name: pos::spanned( span, - Pattern::Ident(TypedIdent::new( - symbols.symbol(format!("show_{}", bind.alias.value.name.declared_name())), - )), + Pattern::Ident(TypedIdent::new(symbols.simple_symbol(format!( + "show_{}", + bind.alias.value.name.declared_name() + )))), ), args: Vec::new(), expr: pos::spanned(span, show_record_expr), diff --git a/vm/src/serialization.rs b/vm/src/serialization.rs index c4556fc003..831e3350c0 100644 --- a/vm/src/serialization.rs +++ b/vm/src/serialization.rs @@ -348,7 +348,7 @@ pub mod symbol { where D: Deserializer<'de>, { - String::deserialize(deserializer).map(|s| seed.symbols.borrow_mut().symbol(s)) + String::deserialize(deserializer).map(|s| seed.symbols.borrow_mut().simple_symbol(s)) } pub fn serialize(symbol: &Symbol, serializer: S, _state: &SeSeed) -> Result diff --git a/vm/src/vm.rs b/vm/src/vm.rs index f89f529cbb..a960f5e942 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -535,10 +535,7 @@ impl GlobalVmState { value: &Value, ) -> Result<()> { assert!(value.generation().is_root()); - assert!( - id.as_ref().matches('@').next() == Some("@"), - "Global symbols must be prefixed with '@'" - ); + assert!(id.is_global(), "Symbol is not global"); let mut env = self.env.write().unwrap(); let globals = &mut env.globals; let global = Global { From 3d835a8adaf2b3c63ef31d512780c9b6de2be5d8 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 13 Aug 2019 20:55:21 +0200 Subject: [PATCH 05/27] perf: Only mark types with unbound generics as HAS_GENERICS --- base/src/ast.rs | 10 ++++++++-- base/src/serialization.rs | 7 +++++-- base/src/types/flags.rs | 25 ++++++++++++++++++++----- base/src/types/mod.rs | 26 ++++++++++++++++++++------ base/tests/types.rs | 8 ++++++++ 5 files changed, 61 insertions(+), 15 deletions(-) diff --git a/base/src/ast.rs b/base/src/ast.rs index 9909e8f774..6692898811 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -188,7 +188,10 @@ pub struct TypedIdent> { } impl TypedIdent { - pub fn new(name: Id) -> TypedIdent { + pub fn new(name: Id) -> TypedIdent + where + Id: PartialEq, + { TypedIdent { typ: Type::hole(), name, @@ -543,7 +546,10 @@ pub struct ValueBinding { pub expr: SpannedExpr, } -impl Default for ValueBinding { +impl Default for ValueBinding +where + T: PartialEq, +{ fn default() -> Self { ValueBinding { metadata: Default::default(), diff --git a/base/src/serialization.rs b/base/src/serialization.rs index bd90d091aa..478f90b042 100644 --- a/base/src/serialization.rs +++ b/base/src/serialization.rs @@ -122,7 +122,10 @@ where } } -impl Shared for ArcType { +impl Shared for ArcType +where + Id: PartialEq, +{ fn unique(&self) -> bool { ArcType::strong_count(self) == 1 } @@ -331,7 +334,7 @@ pub mod seq { impl SerializeState for ArcType where - Id: SerializeState, + Id: SerializeState + PartialEq, { fn serialize_state(&self, serializer: S, seed: &SeSeed) -> Result where diff --git a/base/src/types/flags.rs b/base/src/types/flags.rs index a599ec570a..4d017b2ea7 100644 --- a/base/src/types/flags.rs +++ b/base/src/types/flags.rs @@ -1,4 +1,4 @@ -use super::{Field, Type, TypeExt}; +use super::{Field, FlagsVisitor, Type, TypeExt, Walker}; use bitflags::bitflags; @@ -35,7 +35,8 @@ where impl AddFlags for Field where - T: AddFlags, + T: AddFlags + TypeExt, + Id: PartialEq, { fn add_flags(&self, flags: &mut Flags) { self.typ.add_flags(flags); @@ -53,7 +54,8 @@ where impl AddFlags for Type where - T: AddFlags, + T: AddFlags + TypeExt, + Id: PartialEq, { fn add_flags(&self, flags: &mut Flags) { match self { @@ -68,9 +70,21 @@ where Type::Record(ref typ) | Type::Variant(ref typ) | Type::Effect(ref typ) => { typ.add_flags(flags) } - Type::Forall(_, ref typ) => { + Type::Forall(ref params, ref typ) => { *flags |= Flags::HAS_FORALL; typ.add_flags(flags); + + let mut unbound_generic = false; + &mut FlagsVisitor(Flags::HAS_GENERICS, |typ: &T| match &**typ { + Type::Generic(gen) => { + unbound_generic |= params.iter().all(|param| param.id != gen.id) + } + _ => (), + }) + .walk(typ); + if !unbound_generic { + flags.remove(Flags::HAS_GENERICS); + } } Type::Skolem(_) => *flags |= Flags::HAS_SKOLEMS, Type::ExtendRow { fields, rest, .. } => { @@ -94,7 +108,8 @@ where impl Flags { pub fn from_type(typ: &Type) -> Self where - T: TypeExt, + T: TypeExt + TypeExt, + Id: PartialEq, { let mut flags = Flags::empty(); typ.add_flags(&mut flags); diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 299030dab4..40082add95 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -1301,7 +1301,10 @@ pub struct ArcType { typ: Arc>, } -impl Default for ArcType { +impl Default for ArcType +where + Id: PartialEq, +{ fn default() -> Self { Type::hole() } @@ -1310,7 +1313,7 @@ impl Default for ArcType { #[cfg(feature = "serde")] impl<'de, Id> DeserializeState<'de, Seed>> for ArcType where - Id: DeserializeState<'de, Seed>> + Clone + ::std::any::Any, + Id: DeserializeState<'de, Seed>> + Clone + ::std::any::Any + PartialEq, { fn deserialize_state( seed: &mut Seed>, @@ -1719,7 +1722,10 @@ where }) } -impl TypeExt for ArcType { +impl TypeExt for ArcType +where + Id: PartialEq, +{ type Id = Id; fn new(typ: Type>) -> ArcType { @@ -1768,7 +1774,10 @@ where } } -impl From>> for ArcType { +impl From>> for ArcType +where + Id: PartialEq, +{ fn from(typ: Type>) -> ArcType { ArcType::new(typ) } @@ -2058,7 +2067,7 @@ impl ArcType { pub fn set(into: &mut Self, typ: Type) where - Id: Clone, + Id: Clone + PartialEq, { let into = Arc::make_mut(&mut into.typ); into.flags = Flags::from_type(&typ); @@ -2079,7 +2088,10 @@ impl ArcType { ) } - pub fn needs_generalize(&self) -> bool { + pub fn needs_generalize(&self) -> bool + where + Id: PartialEq, + { self.flags().intersects(Flags::NEEDS_GENERALIZE) } @@ -2941,6 +2953,7 @@ macro_rules! forward_type_interner_methods { ) -> Vec<$crate::types::Alias<$id, $typ>> where $typ: $crate::types::TypeExt, + $id: PartialEq, { $crate::expr!(self, $($tokens)+).alias_group(group, is_implicit_iter) } @@ -3263,6 +3276,7 @@ pub trait TypeContext { ) -> Vec> where T: TypeExt, + Id: PartialEq, { let group = Arc::<[_]>::from(group); (0..group.len()) diff --git a/base/tests/types.rs b/base/tests/types.rs index b8fa721b7f..778d2658f2 100644 --- a/base/tests/types.rs +++ b/base/tests/types.rs @@ -426,3 +426,11 @@ fn resolve_partially_applied_alias() { "String -> Int" ); } + +#[test] +fn forall_hides_generic_flag() { + let a = Generic::new("a", Kind::typ()); + let gen = Type::<_, ArcType<_>>::generic(a.clone()); + assert_eq!(gen.flags(), Flags::HAS_GENERICS); + assert_eq!(Type::forall(vec![a], gen).flags(), Flags::HAS_FORALL); +} From 89eb836afbd2e5d43324c141fad52b761816c754 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 13 Aug 2019 20:55:35 +0200 Subject: [PATCH 06/27] perf: Avoid recursion in implicits.rs --- check/src/implicits.rs | 129 ++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 87 deletions(-) diff --git a/check/src/implicits.rs b/check/src/implicits.rs index c1b6b0ea2a..2b3060fbd5 100644 --- a/check/src/implicits.rs +++ b/check/src/implicits.rs @@ -26,6 +26,19 @@ use crate::{ TypecheckEnv, }; +fn spliterator<'a>( + subs: &'a Substitution, + typ: &'a RcType, +) -> impl Iterator + 'a { + let mut current = Some(typ); + std::iter::from_fn(move || { + let typ = current?; + let (symbol, next) = split_type(subs, typ)?; + current = next; + Some(symbol) + }) +} + fn split_type<'a>( subs: &'a Substitution, typ: &'a RcType, @@ -130,109 +143,53 @@ impl Default for Partition { } impl Partition { - fn insert(&mut self, subs: &Substitution, typ: Option<&RcType>, level: Level, value: T) + fn insert(&mut self, subs: &Substitution, typ: &RcType, level: Level, value: T) where T: Clone, { - self.insert_(subs, typ, level, value); - // Ignore the insertion request at the top level as we know that the partitioning is 100% - // correct here (it matches on the implicit type rather than the argument to the implicit - // type) - } - - fn insert_( - &mut self, - subs: &Substitution, - typ: Option<&RcType>, - level: Level, - value: T, - ) -> bool - where - T: Clone, - { - match typ.and_then(|typ| split_type(subs, typ)) { - Some((symbol, rest)) => { - let partition = self.partition.entry(symbol).or_default(); - if partition.insert_(subs, rest, level, value.clone()) { - // Add a fallback value, ideally we shouldn't need this - partition.rest.push((level, value)); - } - true - } - None => { - self.rest.push((level, value)); - false - } + let mut partition = self; + for symbol in spliterator(subs, typ) { + partition = partition.partition.entry(symbol).or_default(); + partition.rest.push((level, value.clone())); } } - fn remove(&mut self, subs: &Substitution, typ: Option<&RcType>) -> bool { - match typ.and_then(|typ| split_type(subs, typ)) { - Some((symbol, rest)) => { - let partition = self - .partition - .get_mut(&symbol) - .expect("Entry from insert call"); - if partition.remove(subs, rest) { - partition.rest.pop(); - } - true - } - None => { - self.rest.pop(); - false - } + fn remove(&mut self, subs: &Substitution, typ: &RcType) { + let mut partition = self; + for symbol in spliterator(subs, typ) { + partition = partition + .partition + .get_mut(&symbol) + .expect("Entry from insert call"); + partition.rest.pop(); } } fn get_candidates<'a>( &'a self, subs: &Substitution, - typ: Option<&RcType>, + typ: &RcType, implicit_bindings_level: Level, consumer: &mut impl FnMut(&'a T), - ) -> Option<()> - where + ) where T: fmt::Debug, { fn f(t: &(Level, U)) -> &U { &t.1 } - match typ.and_then(|typ| split_type(subs, &typ)) { - Some((symbol, rest)) => { - match self.partition.get(&symbol).and_then(|bindings| { - bindings.get_candidates(subs, rest, implicit_bindings_level, consumer) - }) { - Some(()) => Some(()), - None => { - let end = self - .rest - .iter() - .rposition(|(level, _)| *level <= implicit_bindings_level) - .map_or(0, |i| i + 1); - self.rest[..end].iter().map(f).for_each(consumer); - if end == 0 { - None - } else { - Some(()) - } - } - } - } - None => { - let end = self - .rest - .iter() - .rposition(|(level, _)| *level <= implicit_bindings_level) - .map_or(0, |i| i + 1); - self.rest[..end].iter().map(f).for_each(consumer); - if end == 0 { - None - } else { - Some(()) - } + let mut partition = self; + for symbol in spliterator(subs, typ) { + match self.partition.get(&symbol) { + Some(bindings) => partition = bindings, + None => break, } } + let end = partition + .rest + .iter() + .rposition(|(level, _)| *level <= implicit_bindings_level) + .map_or(0, |i| i + 1); + partition.rest[..end].iter().map(f).for_each(&mut *consumer); } } @@ -287,7 +244,7 @@ impl ImplicitBindings { let level = Level(self.partition_insertions.len().try_into().unwrap()); self.partition - .insert(subs, Some(typ), level, (Rc::from(&path[..]), typ.clone())); + .insert(subs, typ, level, (Rc::from(&path[..]), typ.clone())); self.partition_insertions .push(Some((typ.clone(), definition.cloned()))); @@ -308,9 +265,7 @@ impl ImplicitBindings { ) -> impl DoubleEndedIterator { let mut candidates = Vec::new(); self.partition - .get_candidates(subs, Some(typ), level, &mut |bind| { - candidates.push(bind.clone()) - }); + .get_candidates(subs, typ, level, &mut |bind| candidates.push(bind.clone())); candidates.into_iter() } @@ -325,7 +280,7 @@ impl ImplicitBindings { if let Some(definition) = definition { self.definitions.remove(&definition); } - self.partition.remove(subs, Some(&typ)); + self.partition.remove(subs, &typ); } } } From d69a52e0a73853d48d0a83b5635e10caa90b6d7b Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 13 Aug 2019 22:08:20 +0200 Subject: [PATCH 07/27] Unknown --- base/src/types/mod.rs | 41 ++++++++++++++++++++++++++++------------- check/src/implicits.rs | 14 ++------------ check/src/typecheck.rs | 18 +++++++++++------- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 40082add95..019b0702f8 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -628,6 +628,7 @@ pub struct AliasData { /// The type that is being aliased #[cfg_attr(feature = "serde_derive", serde(state))] typ: T, + pub is_implicit: bool, } impl AliasData { @@ -640,6 +641,10 @@ impl AliasData { pub fn unresolved_type_mut(&mut self) -> &mut T { &mut self.typ } + + pub fn is_implicit(&self) -> bool { + self.is_implicit + } } impl AliasData @@ -647,7 +652,12 @@ where T: From>, { pub fn new(name: Id, args: Vec>, typ: T) -> AliasData { - AliasData { name, args, typ } + AliasData { + name, + args, + typ, + is_implicit: false, + } } } @@ -1004,7 +1014,12 @@ where pub fn alias(name: Id, args: Vec>, typ: T) -> T { T::from(Type::Alias(AliasRef { index: 0, - group: Arc::from(vec![AliasData { name, args, typ }]), + group: Arc::from(vec![AliasData { + name, + args, + typ, + is_implicit: false, + }]), })) } @@ -2949,13 +2964,12 @@ macro_rules! forward_type_interner_methods { fn alias_group( &mut self, group: Vec<$crate::types::AliasData<$id, $typ>>, - is_implicit_iter: impl IntoIterator ) -> Vec<$crate::types::Alias<$id, $typ>> where $typ: $crate::types::TypeExt, $id: PartialEq, { - $crate::expr!(self, $($tokens)+).alias_group(group, is_implicit_iter) + $crate::expr!(self, $($tokens)+).alias_group(group) } /* @@ -3200,7 +3214,12 @@ pub trait TypeContext { fn alias(&mut self, name: Id, args: Vec>, typ: T) -> T { self.intern(Type::Alias(AliasRef { index: 0, - group: Arc::from(vec![AliasData { name, args, typ }]), + group: Arc::from(vec![AliasData { + name, + args, + typ, + is_implicit: false, + }]), })) } @@ -3269,25 +3288,20 @@ pub trait TypeContext { } } - fn alias_group( - &mut self, - group: Vec>, - is_implicit_iter: impl IntoIterator, - ) -> Vec> + fn alias_group(&mut self, group: Vec>) -> Vec> where T: TypeExt, Id: PartialEq, { let group = Arc::<[_]>::from(group); (0..group.len()) - .zip(is_implicit_iter) - .map(|(index, is_implicit)| { + .map(|index| { let typ = Type::Alias(AliasRef { index, group: group.clone(), }); let flags = Flags::from_type(&typ) - | (if is_implicit { + | (if group[index].is_implicit { Flags::HAS_IMPLICIT } else { Flags::empty() @@ -3863,6 +3877,7 @@ where name: alias.name.clone(), args: alias.args.clone(), typ: translate(&alias.typ), + is_implicit: alias.is_implicit, } } diff --git a/check/src/implicits.rs b/check/src/implicits.rs index 2b3060fbd5..33f45bff0c 100644 --- a/check/src/implicits.rs +++ b/check/src/implicits.rs @@ -709,7 +709,6 @@ pub struct ImplicitResolver<'a> { pub(crate) implicit_vars: ScopedMap, visited: ScopedMap, Box<[RcType]>>, alias_resolver: resolve::AliasRemover, - is_implicit_memo: RefCell>, path: Vec>, } @@ -725,7 +724,6 @@ impl<'a> ImplicitResolver<'a> { implicit_vars: ScopedMap::new(), visited: Default::default(), alias_resolver: resolve::AliasRemover::new(), - is_implicit_memo: Default::default(), path: Vec::new(), } } @@ -869,16 +867,8 @@ impl<'a> ImplicitResolver<'a> { is_implicit = iter .typ .remove_forall() - .owned_name() - .map_or(false, |typename| { - let mut is_implicit_memo = self.is_implicit_memo.borrow_mut(); - *is_implicit_memo.entry(typename.clone()).or_insert_with(|| { - self.metadata - .get(&*typename) - .or_else(|| self.environment.get_metadata(&typename)) - .map_or(false, |m| has_implicit_attribute(m)) - }) - }); + .applied_alias() + .map_or(false, |alias| alias.is_implicit()); } if is_implicit { diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index 8e295cbb90..fa71a3a220 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -2144,15 +2144,19 @@ impl<'a> Typecheck<'a> { let arc_alias_group = Alias::group( resolved_aliases .iter() - .map(|a| types::translate_alias(&a, |t| self.translate_rc_type(t))) + .zip( + bindings + .iter() + .map(|bind| bind.metadata.get_attribute("implicit").is_some()), + ) + .map(|(a, is_implicit)| { + let mut alias_data = types::translate_alias(&a, |t| self.translate_rc_type(t)); + alias_data.is_implicit = is_implicit; + alias_data + }) .collect(), ); - let alias_group = self.subs.alias_group( - resolved_aliases, - bindings - .iter() - .map(|bind| bind.metadata.get_attribute("implicit").is_some()), - ); + let alias_group = self.subs.alias_group(resolved_aliases); for (bind, alias) in bindings.iter_mut().zip(arc_alias_group) { bind.finalized_alias = Some(alias); } From 4a3662e9e108b902ed61f17d9e2da204d29e54bf Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Tue, 13 Aug 2019 22:51:45 +0200 Subject: [PATCH 08/27] perf(check): Avoid looking through metadata when checking for an implicit type --- base/src/types/mod.rs | 6 +++++- check/src/implicits.rs | 27 ++++++++++++--------------- check/src/typecheck.rs | 13 +++---------- check/tests/implicits.rs | 2 +- check/tests/support/mod.rs | 18 ++++++++++++++++-- 5 files changed, 37 insertions(+), 29 deletions(-) diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 019b0702f8..4fe2bc83af 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -1012,13 +1012,17 @@ where } pub fn alias(name: Id, args: Vec>, typ: T) -> T { + Self::alias_implicit(name, args, typ, false) + } + + pub fn alias_implicit(name: Id, args: Vec>, typ: T, is_implicit: bool) -> T { T::from(Type::Alias(AliasRef { index: 0, group: Arc::from(vec![AliasData { name, args, typ, - is_implicit: false, + is_implicit, }]), })) } diff --git a/check/src/implicits.rs b/check/src/implicits.rs index 33f45bff0c..1a56b2a15f 100644 --- a/check/src/implicits.rs +++ b/check/src/implicits.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, convert::TryInto, fmt, rc::Rc, sync::Arc}; +use std::{convert::TryInto, fmt, rc::Rc, sync::Arc}; use {itertools::Itertools, smallvec::SmallVec}; @@ -856,20 +856,17 @@ impl<'a> ImplicitResolver<'a> { metadata: Option<&'m Metadata>, typ: &RcType, ) -> Option> { - let has_implicit_attribute = - |metadata: &Metadata| metadata.get_attribute("implicit").is_some(); - let mut is_implicit = metadata.map(&has_implicit_attribute).unwrap_or(false); - - if !is_implicit { - // Look at the type without any implicit arguments - let mut iter = types::implicit_arg_iter(typ.remove_forall()); - for _ in iter.by_ref() {} - is_implicit = iter - .typ - .remove_forall() - .applied_alias() - .map_or(false, |alias| alias.is_implicit()); - } + // Look at the type without any implicit arguments + let mut iter = types::implicit_arg_iter(typ.remove_forall()); + for _ in iter.by_ref() {} + let is_implicit = iter + .typ + .remove_forall() + .applied_alias() + .map_or(false, |alias| alias.is_implicit()) + || metadata + .and_then(|metadata: &Metadata| metadata.get_attribute("implicit")) + .is_some(); if is_implicit { Some(metadata.and_then(|m| m.definition.as_ref())) diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index fa71a3a220..fed212e28c 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -2123,6 +2123,8 @@ impl<'a> Typecheck<'a> { let mut alias = types::translate_alias(&bind.alias.value, |typ| self.translate_ast_type(typ)); + alias.is_implicit = bind.metadata.get_attribute("implicit").is_some(); + let replacement = self.create_unifiable_signature_with( // alias.unresolved_type() is a dummy in this context alias @@ -2144,16 +2146,7 @@ impl<'a> Typecheck<'a> { let arc_alias_group = Alias::group( resolved_aliases .iter() - .zip( - bindings - .iter() - .map(|bind| bind.metadata.get_attribute("implicit").is_some()), - ) - .map(|(a, is_implicit)| { - let mut alias_data = types::translate_alias(&a, |t| self.translate_rc_type(t)); - alias_data.is_implicit = is_implicit; - alias_data - }) + .map(|a| types::translate_alias(&a, |t| self.translate_rc_type(t))) .collect(), ); let alias_group = self.subs.alias_group(resolved_aliases); diff --git a/check/tests/implicits.rs b/check/tests/implicits.rs index fa16d50c85..edba3eb27b 100644 --- a/check/tests/implicits.rs +++ b/check/tests/implicits.rs @@ -212,7 +212,7 @@ f (Test ()) "#; let result = support::typecheck(text); - let test = support::alias_variant("Test", &[], &[("Test", &[Type::unit()])]); + let test = support::alias_variant_implicit("Test", &[], &[("Test", &[Type::unit()])], true); assert_eq!(result, Ok(test)); } diff --git a/check/tests/support/mod.rs b/check/tests/support/mod.rs index 09c8c0679b..d594424359 100644 --- a/check/tests/support/mod.rs +++ b/check/tests/support/mod.rs @@ -295,13 +295,18 @@ where #[allow(dead_code)] pub fn alias(s: &str, args: &[&str], typ: ArcType) -> ArcType { + alias_implicit(s, args, typ, false) +} + +pub fn alias_implicit(s: &str, args: &[&str], typ: ArcType, is_implicit: bool) -> ArcType { assert!(s.len() != 0); - Type::alias( + Type::alias_implicit( intern(s), args.iter() .map(|id| Generic::new(intern(id), Kind::typ())) .collect(), typ, + is_implicit, ) } @@ -311,12 +316,21 @@ pub fn variant(arg: &str, types: &[ArcType]) -> Field { } pub fn alias_variant(s: &str, params: &[&str], args: &[(&str, &[ArcType])]) -> ArcType { + alias_variant_implicit(s, params, args, false) +} + +pub fn alias_variant_implicit( + s: &str, + params: &[&str], + args: &[(&str, &[ArcType])], + is_implicit: bool, +) -> ArcType { let variants = Type::variant( args.iter() .map(|(arg, types)| variant(arg, types)) .collect(), ); - alias(s, params, variants) + alias_implicit(s, params, variants, is_implicit) } /// Replace the variable at the `rest` part of a record for easier equality checks From de32dbd631beb0958c7c0fcb655807fda874a2a7 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Aug 2019 21:14:55 +0200 Subject: [PATCH 09/27] perf: Avoid RefCell in Fixed* structurs (-1%) --- base/src/fixed.rs | 156 +++++++++++++++++++++++++++--------------- base/src/types/mod.rs | 14 +++- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/base/src/fixed.rs b/base/src/fixed.rs index 8e8645bbbb..5382346a56 100644 --- a/base/src/fixed.rs +++ b/base/src/fixed.rs @@ -3,7 +3,7 @@ //! earlier inserted value to be overwritten. use std::{ borrow::Borrow, - cell::RefCell, + cell::UnsafeCell, collections::hash_map::Entry, fmt, hash::Hash, @@ -21,15 +21,45 @@ use vec_map::VecMap; // can only be inserted, never modified (this could be done with a Rc pointer as well but // is not done for efficiency since it is not needed) -unsafe fn forget_lifetime<'a, 'b, T: ?Sized>(x: &'a T) -> &'b T { - ::std::mem::transmute(x) +// UnsafeCell makes sure we are `!Sync` +#[derive(Default)] +struct MutCell(UnsafeCell); + +impl fmt::Debug for MutCell { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.get().fmt(f) + } +} + +impl MutCell { + fn new(t: T) -> Self { + MutCell(UnsafeCell::new(t)) + } +} + +impl MutCell { + fn get(&self) -> &T { + // Getting a shared reference from a shared reference is safe + unsafe { &*self.0.get() } + } + + // We can get a mutable reference as long as we make sure to not have any `&T` references alive + // for the time that we use the mutable refernce + unsafe fn unsafe_get_mut(&self) -> &mut T { + &mut *self.0.get() + } + + fn get_mut(&mut self) -> &mut T { + // Getting a mutable reference from a mutable reference is safe + unsafe { self.unsafe_get_mut() } + } } // A mapping between K and V where once a value has been inserted it cannot be changed // Through this and the fact the all values are stored as pointers it is possible to safely // insert new values without invalidating pointers retrieved from it pub struct FixedMap { - map: RefCell>, + map: MutCell>, values: Buffer, } @@ -41,24 +71,24 @@ impl Default for FixedMap { impl fmt::Debug for FixedMap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.map.borrow().fmt(f) + self.map.get().fmt(f) } } impl FixedMap { pub fn new() -> FixedMap { FixedMap { - map: RefCell::new(FnvMap::default()), + map: Default::default(), values: Default::default(), } } pub fn clear(&mut self) { - self.map.borrow_mut().clear(); + self.map.get_mut().clear(); } pub fn len(&self) -> usize { - self.map.borrow().len() + self.map.get().len() } pub fn is_empty(&self) -> bool { @@ -82,7 +112,11 @@ impl FixedMap { Err((key, value)) } else { let index_value = self.values.push(value); - self.map.borrow_mut().insert(key, index_value); + // SAFETY This effectively works as a RefCell since the mutable reference is limited to + // this module + unsafe { + self.map.unsafe_get_mut().insert(key, index_value); + } Ok(()) } } @@ -92,7 +126,7 @@ impl FixedMap { K: Borrow, Q: ?Sized + Eq + Hash, { - self.map.borrow().get(k).map(|key| &self.values[*key]) + self.map.get().get(k).map(|key| &self.values[*key]) } pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> @@ -130,7 +164,7 @@ where // insert new values without invalidating pointers retrieved from it pub struct FixedVecMap { // Use u16 to leave space for the `Option` tag in `VecMap` - map: RefCell>, + map: MutCell>, values: Buffer, } @@ -142,7 +176,7 @@ impl Default for FixedVecMap { impl fmt::Debug for FixedVecMap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.map.borrow().fmt(f) + self.map.get().fmt(f) } } @@ -155,11 +189,11 @@ impl FixedVecMap { } pub fn clear(&mut self) { - self.map.borrow_mut().clear(); + self.map.get_mut().clear(); } pub fn len(&self) -> usize { - self.map.borrow().len() + self.map.get().len() } pub fn is_empty(&self) -> bool { @@ -190,13 +224,17 @@ impl FixedVecMap { Err((key, value)) } else { let (i, j) = self.values.push(value); - self.map.borrow_mut().insert(key, (i as u16, j)); + // SAFETY This effectively works as a RefCell since the mutable reference is limited to + // this module + unsafe { + self.map.unsafe_get_mut().insert(key, (i as u16, j)); + } Ok(()) } } pub fn get(&self, k: usize) -> Option<&V> { - self.map.borrow().get(k).map(|key| &self.values[*key]) + self.map.get().get(k).map(|key| &self.values[*key]) } pub fn get_mut(&mut self, k: usize) -> Option<&mut V> { @@ -216,7 +254,7 @@ impl FixedVecMap { pub fn iter(&self) -> impl Iterator { self.map - .borrow() + .get() .iter() .map(|(k, v)| (k, *v)) .collect::>() @@ -234,7 +272,7 @@ impl Index for FixedVecMap { #[derive(Debug)] pub struct FixedVec { - vec: RefCell>, + vec: MutCell>, values: Buffer, } @@ -247,18 +285,22 @@ impl Default for FixedVec { impl FixedVec { pub fn new() -> FixedVec { FixedVec { - vec: RefCell::new(Vec::new()), + vec: MutCell::new(Vec::new()), values: Default::default(), } } pub fn clear(&mut self) { - self.vec.borrow_mut().clear(); + self.vec.get_mut().clear(); } pub fn push(&self, value: T) { let key = self.values.push(value); - self.vec.borrow_mut().push(key); + // SAFETY This effectively works as a RefCell since the mutable reference is limited to + // this module + unsafe { + self.vec.unsafe_get_mut().push(key); + } } pub fn extend>(&self, iter: I) { @@ -268,7 +310,7 @@ impl FixedVec { } pub fn len(&self) -> usize { - self.vec.borrow().len() + self.vec.get().len() } pub fn is_empty(&self) -> bool { @@ -304,14 +346,14 @@ impl FixedVec { impl Index for FixedVec { type Output = T; fn index(&self, index: usize) -> &T { - let vec = self.vec.borrow(); + let vec = self.vec.get(); &self.values[vec[index]] } } impl IndexMut for FixedVec { fn index_mut(&mut self, index: usize) -> &mut T { - let vec = self.vec.borrow(); + let vec = self.vec.get(); &mut self.values[vec[index]] } } @@ -324,7 +366,7 @@ struct BufferInner { #[derive(Debug)] struct Buffer { - values: RefCell>, + values: MutCell>, } impl Default for BufferInner { @@ -350,36 +392,40 @@ impl Buffer { } fn total_len(&self) -> usize { - let values = self.values.borrow(); + let values = self.values.get(); values.values.iter().map(|vec| vec.len()).sum::() } fn push(&self, value: T) -> (u32, u32) { - let mut values = self.values.borrow_mut(); - let cap = match values.current().map(|vec| (vec.len(), vec.capacity())) { - Some((len, capacity)) => { - if len == capacity { - values.current += 1; - if values.current + 1 < values.values.len() { - None + // SAFETY This effectively works as a RefCell since the mutable reference is limited to + // this module + unsafe { + let mut values = self.values.unsafe_get_mut(); + let cap = match values.current().map(|vec| (vec.len(), vec.capacity())) { + Some((len, capacity)) => { + if len == capacity { + values.current += 1; + if values.current + 1 < values.values.len() { + None + } else { + Some(capacity * 2) + } } else { - Some(capacity * 2) + None } - } else { - None } + None => Some(4), + }; + if let Some(cap) = cap { + values.values.push(Vec::with_capacity(cap)); } - None => Some(4), - }; - if let Some(cap) = cap { - values.values.push(Vec::with_capacity(cap)); - } - let i = values.current as u32; - let inner = values.current_mut().unwrap(); - let j = inner.len() as u32; - inner.push(value); - (i, j) + let i = values.current as u32; + let inner = values.current_mut().unwrap(); + let j = inner.len() as u32; + inner.push(value); + (i, j) + } } fn truncate(&mut self, index: usize) { @@ -431,17 +477,13 @@ impl BufferInner { impl Index<(u32, u32)> for Buffer { type Output = T; fn index(&self, (i, j): (u32, u32)) -> &T { - unsafe { - forget_lifetime( - &self - .values - .borrow() - .values - .get(i as usize) - .and_then(|v| v.get(j as usize)) - .unwrap_or_else(|| panic!("Index out of bounds: {:?}", (i, j))), - ) - } + &self + .values + .get() + .values + .get(i as usize) + .and_then(|v| v.get(j as usize)) + .unwrap_or_else(|| panic!("Index out of bounds: {:?}", (i, j))) } } diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 4fe2bc83af..3ed776c31d 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -1256,12 +1256,24 @@ where } } -#[derive(Eq, Clone, Debug)] +#[derive(Eq, Clone)] pub enum SymbolKey { Owned(Symbol), Ref(&'static SymbolRef), } +impl fmt::Debug for SymbolKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", Borrow::::borrow(self)) + } +} + +impl fmt::Display for SymbolKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", Borrow::::borrow(self)) + } +} + impl Hash for SymbolKey { fn hash(&self, state: &mut H) where From a9c965b418cbff642d5c8a0c9c3c7821b610eb12 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Aug 2019 21:16:00 +0200 Subject: [PATCH 10/27] perf(check): Narrow down the implicit parititioning further (-10%) --- check/src/implicits.rs | 62 ++++++++++++++++++++++++++++++----------- check/src/unify_type.rs | 1 - 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/check/src/implicits.rs b/check/src/implicits.rs index 1a56b2a15f..d33faf74f6 100644 --- a/check/src/implicits.rs +++ b/check/src/implicits.rs @@ -43,26 +43,20 @@ fn split_type<'a>( subs: &'a Substitution, typ: &'a RcType, ) -> Option<(SymbolKey, Option<&'a RcType>)> { - let symbol = match **subs.real(typ) { - Type::App(ref id, ref args) => { - return split_type(subs, id) - .map(|(k, _)| k) - .map(|key| (key, if args.len() == 1 { args.get(0) } else { None })); + let symbol = match &**subs.real(typ) { + Type::App(id, args) => { + return split_type(subs, id).map(|(key, _)| (key, args.get(0))); } - Type::Function(ArgType::Implicit, _, ref ret_type) => { - split_type(subs, ret_type) - // Usually the implicit argument will appear directly inside type whose `SymbolKey` - // that was returned so it is unlikely that partitition further - .map(|(s, _)| s) + Type::Forall(_, typ) | Type::Function(ArgType::Implicit, _, typ) => { + return split_type(subs, typ) } Type::Function(ArgType::Explicit, ..) => { Some(SymbolKey::Ref(BuiltinType::Function.symbol())) } - Type::Skolem(ref skolem) => Some(SymbolKey::Owned(skolem.name.clone())), - Type::Ident(ref id) => Some(SymbolKey::Owned(id.clone())), - Type::Alias(ref alias) => Some(SymbolKey::Owned(alias.name.clone())), - Type::Builtin(ref builtin) => Some(SymbolKey::Ref(builtin.symbol())), - Type::Forall(_, ref typ) => return split_type(subs, typ), + Type::Skolem(skolem) => Some(SymbolKey::Owned(skolem.name.clone())), + Type::Ident(id) => Some(SymbolKey::Owned(id.clone())), + Type::Alias(alias) => Some(SymbolKey::Owned(alias.name.clone())), + Type::Builtin(builtin) => Some(SymbolKey::Ref(builtin.symbol())), _ => None, }; symbol.map(|s| (s, None)) @@ -179,7 +173,7 @@ impl Partition { } let mut partition = self; for symbol in spliterator(subs, typ) { - match self.partition.get(&symbol) { + match partition.partition.get(&symbol) { Some(bindings) => partition = bindings, None => break, } @@ -927,3 +921,39 @@ fn smallers(state: unify_type::State, new_types: &[RcType], old_types: &[RcType] == Size::Smaller } } + +#[cfg(test)] +mod tests { + use super::*; + + use base::{kind::Kind, types::SharedInterner}; + + use crate::tests::*; + + #[test] + fn spliterator_test() { + let interner = SharedInterner::default(); + let subs = Substitution::new(Kind::typ(), interner); + + let a = intern("A"); + let b = intern("B"); + + let typ = Type::app(Type::ident(a.clone()), collect![Type::ident(b.clone())]); + assert_eq!( + spliterator(&subs, &typ).collect::>(), + vec![SymbolKey::Owned(a.clone()), SymbolKey::Owned(b.clone())] + ); + + let typ = Type::app( + Type::ident(a.clone()), + collect![Type::app( + Type::ident(b.clone()), + collect![Type::generic(Generic::new(intern("c"), Kind::typ()))] + )], + ); + assert_eq!( + spliterator(&subs, &typ).collect::>(), + vec![SymbolKey::Owned(a), SymbolKey::Owned(b)] + ); + } +} diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 6333f7b2b7..03733658f7 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -979,7 +979,6 @@ where return Err(()); } } - debug!("Looking for alias reduction from `{}` to `{}`", l_id, r_id); if l_id == r_id { // If the aliases matched before going through an alias there is no need to // return a replacement type From 0ea13ff1923527bbc69a5fa44ce905fe2a4c593d Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Aug 2019 23:10:18 +0200 Subject: [PATCH 11/27] perf(check): Only do one lookup/insertion on the implicit definition map --- check/src/implicits.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/check/src/implicits.rs b/check/src/implicits.rs index d33faf74f6..548c7811d6 100644 --- a/check/src/implicits.rs +++ b/check/src/implicits.rs @@ -231,10 +231,6 @@ impl ImplicitBindings { path: &[TypedIdent], typ: &RcType, ) { - if let Some(definition) = definition { - self.definitions.insert(definition.clone(), ()); - } - let level = Level(self.partition_insertions.len().try_into().unwrap()); self.partition @@ -770,7 +766,11 @@ impl<'a> ImplicitResolver<'a> { // add it again to prevent ambiguities if let Some(metadata) = metadata { if let Some(ref definition) = metadata.definition { - if self.implicit_bindings.definitions.contains_key(definition) { + if self + .implicit_bindings + .definitions + .insert(definition.clone(), ()) + { return; } } From 793b658082f71f016655f7f4f3e44432814116fa Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Aug 2019 23:10:58 +0200 Subject: [PATCH 12/27] perf(check): Only initialize the variable generator when it is necessary (-3%) --- check/src/typecheck.rs | 12 +++++++----- check/src/typecheck/generalize.rs | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index fed212e28c..5f5524e308 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -25,7 +25,7 @@ use crate::base::{ symbol::{Symbol, SymbolModule, SymbolRef, Symbols}, types::{ self, Alias, AliasRef, AppVec, ArcType, ArgType, Field, Flags, Generic, PrimitiveEnv, Type, - TypeCache, TypeContext, TypeEnv, TypeExt, + TypeCache, TypeContext, TypeEnv, TypeExt, Walker, }, }; @@ -1445,7 +1445,7 @@ impl<'a> Typecheck<'a> { } let mut modifier = TypeModifier::Rigid; - types::walk_type(&self.subs.zonk(&original_func_type), &mut |typ: &RcType| { + types::FlagsVisitor(Flags::HAS_VARIABLES, |typ: &RcType| { if modifier == TypeModifier::Rigid { if let Type::Variable(var) = &**typ { if !return_variables.contains(&var.id) { @@ -1453,7 +1453,8 @@ impl<'a> Typecheck<'a> { } } } - }); + }) + .walk(&self.subs.zonk(&original_func_type)); Ok(TailCall::Type(ModType::new(modifier, func_type))) } @@ -2913,7 +2914,7 @@ impl<'a> Typecheck<'a> { let new_type = f(self, ModType::new(original_type.modifier, skolemized)); let original_type = self.subs.zonk(&original_type); - types::walk_type(&original_type, &mut |typ: &RcType| { + types::FlagsVisitor(Flags::HAS_SKOLEMS, |typ: &RcType| { if let Type::Skolem(skolem) = &**typ { if !self.environment.skolem_variables.contains_key(&skolem.name) { self.error( @@ -2925,7 +2926,8 @@ impl<'a> Typecheck<'a> { ); } } - }); + }) + .walk(&original_type); self.with_forall(&original_type, new_type) } diff --git a/check/src/typecheck/generalize.rs b/check/src/typecheck/generalize.rs index 18284dff24..fe39cb6788 100644 --- a/check/src/typecheck/generalize.rs +++ b/check/src/typecheck/generalize.rs @@ -14,7 +14,8 @@ pub(crate) struct TypeGeneralizer<'a, 'b: 'a> { /// We delay updating the substitution until after all recursive bindings have been typechecked /// to ensure that they get can figure out which variable got generalized for each binding delayed_generalizations: Vec<(u32, RcType)>, - variable_generator: TypeVariableGenerator, + variable_generator: Option, + top_type: RcType, pub tc: &'a mut Typecheck<'b>, span: Span, } @@ -55,7 +56,8 @@ impl<'a, 'b> TypeGeneralizer<'a, 'b> { level, unbound_variables: Default::default(), delayed_generalizations: Vec::new(), - variable_generator: TypeVariableGenerator::new(&tc.subs, typ), + variable_generator: None, + top_type: typ.clone(), tc, span, } @@ -152,9 +154,18 @@ impl<'a, 'b> TypeGeneralizer<'a, 'b> { let Self { tc, variable_generator, + top_type, .. } = self; let gen = self.unbound_variables.entry(var.id).or_insert_with(|| { + let variable_generator = match variable_generator { + Some(v) => v, + None => { + *variable_generator = + Some(TypeVariableGenerator::new(&tc.subs, top_type)); + variable_generator.as_mut().unwrap() + } + }; // Create a prefix if none exists let id = variable_generator.next_variable(tc); @@ -249,7 +260,7 @@ impl TypeVariableGenerator { gather_foralls(&mut map, subs, typ); TypeVariableGenerator { map, - name: "".to_string(), + name: String::new(), i: 0, } } From 48a5313ebea8751a3815d2664a1ab001b399a8cb Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Wed, 14 Aug 2019 23:20:36 +0200 Subject: [PATCH 13/27] perf(vm): Avoid tracing global values unless we are in the root gc (-7%) The global state only contains values from the root gc --- vm/src/thread.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vm/src/thread.rs b/vm/src/thread.rs index e3ea8651c2..6553762be7 100644 --- a/vm/src/thread.rs +++ b/vm/src/thread.rs @@ -1009,7 +1009,9 @@ impl Thread { } fn trace_fields_except_stack(&self, gc: &mut Gc) { - self.global_state.trace(gc); + if gc.generation().is_root() { + self.global_state.trace(gc); + } self.rooted_values.read().unwrap().trace(gc); self.child_threads.read().unwrap().trace(gc); } From 178180f8b4555c8ae831d428fc464cb0a4179455 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Thu, 15 Aug 2019 21:23:51 +0200 Subject: [PATCH 14/27] perf: Shrink Type's size to 48 bytes (from 64) --- base/src/ast.rs | 13 +- base/src/resolve.rs | 1 + base/src/types/flags.rs | 5 +- base/src/types/mod.rs | 363 ++++++++++++++++++------------ check/src/kindcheck.rs | 36 ++- check/src/rename.rs | 8 +- check/src/typecheck.rs | 52 ++--- check/src/typecheck/generalize.rs | 43 ++-- check/src/unify_type.rs | 53 +++-- check/tests/row_polymorphism.rs | 8 +- check/tests/support/mod.rs | 7 +- completion/src/lib.rs | 2 +- completion/tests/support/mod.rs | 7 +- parser/src/grammar.lalrpop | 10 +- vm/src/api/mod.rs | 61 +++-- vm/src/api/record.rs | 2 +- 16 files changed, 381 insertions(+), 290 deletions(-) diff --git a/base/src/ast.rs b/base/src/ast.rs index 6692898811..f68475e0fe 100644 --- a/base/src/ast.rs +++ b/base/src/ast.rs @@ -815,18 +815,23 @@ pub fn walk_ast_type<'a, V: ?Sized + $trait_name<'a>>( Type::Effect(ref $($mut)* ast_type) => v.visit_ast_type(&$($mut)* ast_type._typ.typ), Type::EmptyRow => (), Type::ExtendRow { - ref $($mut)* types, ref $($mut)* fields, ref $($mut)* rest, + } => { + for field in fields { + v.visit_ast_type(&$($mut)* field.typ._typ.typ); + } + v.visit_ast_type(&$($mut)* rest._typ.typ); + } + Type::ExtendTypeRow { + ref $($mut)* types, + ref $($mut)* rest, } => { for field in types { if let Some(alias) = field.typ.$try_get_alias() { v.visit_ast_type(&$($mut)* alias.$unresolved_type()._typ.typ); } } - for field in fields { - v.visit_ast_type(&$($mut)* field.typ._typ.typ); - } v.visit_ast_type(&$($mut)* rest._typ.typ); } Type::Alias(ref $($mut)* alias) => { diff --git a/base/src/resolve.rs b/base/src/resolve.rs index 8655e2a37d..1101dcc86c 100644 --- a/base/src/resolve.rs +++ b/base/src/resolve.rs @@ -116,6 +116,7 @@ impl AliasRemover { | Type::Effect(..) | Type::EmptyRow | Type::ExtendRow { .. } + | Type::ExtendTypeRow { .. } if args.is_empty() => { return Ok(typ) diff --git a/base/src/types/flags.rs b/base/src/types/flags.rs index 4d017b2ea7..261a813666 100644 --- a/base/src/types/flags.rs +++ b/base/src/types/flags.rs @@ -87,10 +87,13 @@ where } } Type::Skolem(_) => *flags |= Flags::HAS_SKOLEMS, - Type::ExtendRow { fields, rest, .. } => { + Type::ExtendRow { fields, rest } => { fields.add_flags(flags); rest.add_flags(flags); } + Type::ExtendTypeRow { rest, .. } => { + rest.add_flags(flags); + } Type::Variable(_) => *flags |= Flags::HAS_VARIABLES, Type::Generic(_) => *flags |= Flags::HAS_GENERICS, Type::Ident(_) => *flags |= Flags::HAS_IDENTS, diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 3ed776c31d..5356d59016 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -811,9 +811,6 @@ pub enum Type> { EmptyRow, /// Row extension, of kind `... -> Row -> Row` ExtendRow { - /// The associated types of this record type - #[cfg_attr(feature = "serde_derive", serde(state))] - types: Vec>>, /// The fields of this record type #[cfg_attr(feature = "serde_derive", serde(state))] fields: Vec>, @@ -821,6 +818,15 @@ pub enum Type> { #[cfg_attr(feature = "serde_derive", serde(state))] rest: T, }, + /// Row extension, of kind `... -> Row -> Row` + ExtendTypeRow { + /// The associated types of this record type + #[cfg_attr(feature = "serde_derive", serde(state))] + types: Vec>>, + /// The rest of the row + #[cfg_attr(feature = "serde_derive", serde(state))] + rest: T, + }, /// An identifier type. These are created during parsing, but should all be /// resolved into `Type::Alias`es during type checking. /// @@ -845,6 +851,9 @@ pub enum Type> { Skolem(#[cfg_attr(feature = "serde_derive", serde(state))] Skolem), } +// Safeguard against accidentally growing Type as it is a core type +const _: [(); 8 * 6] = [(); std::mem::size_of::>()]; + impl Type { pub fn as_variable(&self) -> Option<&TypeVariable> { match *self { @@ -903,7 +912,7 @@ where } pub fn poly_variant(fields: Vec>, rest: T) -> T { - T::from(Type::Variant(Type::extend_row(Vec::new(), fields, rest))) + T::from(Type::Variant(Type::extend_row(fields, rest))) } pub fn effect(fields: Vec>) -> T { @@ -911,7 +920,7 @@ where } pub fn poly_effect(fields: Vec>, rest: T) -> T { - T::from(Type::Effect(Type::extend_row(Vec::new(), fields, rest))) + T::from(Type::Effect(Type::extend_row(fields, rest))) } pub fn tuple(symbols: &mut S, elems: I) -> T @@ -928,7 +937,6 @@ where I: IntoIterator, { Type::Record(Type::extend_row( - vec![], elems .into_iter() .enumerate() @@ -950,22 +958,30 @@ where fields: Vec>, rest: T, ) -> T { - T::from(Type::Record(Type::extend_row(types, fields, rest))) + T::from(Type::Record(Type::extend_full_row(types, fields, rest))) } - pub fn extend_row( + pub fn extend_full_row( types: Vec>>, fields: Vec>, rest: T, ) -> T { - if types.is_empty() && fields.is_empty() { + Self::extend_type_row(types, Self::extend_row(fields, rest)) + } + + pub fn extend_row(fields: Vec>, rest: T) -> T { + if fields.is_empty() { rest } else { - T::from(Type::ExtendRow { - types, - fields, - rest, - }) + T::from(Type::ExtendRow { fields, rest }) + } + } + + pub fn extend_type_row(types: Vec>>, rest: T) -> T { + if types.is_empty() { + rest + } else { + T::from(Type::ExtendTypeRow { types, rest }) } } @@ -1146,9 +1162,9 @@ where pub fn is_non_polymorphic_record(&self) -> bool { match *self { - Type::Record(ref row) | Type::ExtendRow { rest: ref row, .. } => { - row.is_non_polymorphic_record() - } + Type::Record(ref row) + | Type::ExtendRow { rest: ref row, .. } + | Type::ExtendTypeRow { rest: ref row, .. } => row.is_non_polymorphic_record(), Type::EmptyRow => true, _ => false, } @@ -1174,7 +1190,9 @@ where Type::Opaque | Type::Builtin(_) | Type::Record(_) | Type::Variant(_) => { Cow::Owned(Kind::typ()) } - Type::EmptyRow | Type::ExtendRow { .. } => Cow::Owned(Kind::row()), + Type::EmptyRow | Type::ExtendRow { .. } | Type::ExtendTypeRow { .. } => { + Cow::Owned(Kind::row()) + } Type::Effect(_) => { let t = Kind::typ(); Cow::Owned(Kind::function(t.clone(), t)) @@ -1834,14 +1852,13 @@ where fn next(&mut self) -> Option<&'a Field>> { match **self.typ { - Type::Record(ref row) => { - self.typ = row; + Type::ExtendRow { ref rest, .. } | Type::Record(ref rest) => { + self.typ = rest; self.next() } - Type::ExtendRow { + Type::ExtendTypeRow { ref types, ref rest, - .. } => { let current = self.current; self.current += 1; @@ -1893,6 +1910,10 @@ where self.next() }) } + Type::ExtendTypeRow { ref rest, .. } => { + self.typ = rest; + self.next() + } _ => None, } } @@ -1921,6 +1942,7 @@ where size += fields.len(); rest } + Type::ExtendTypeRow { ref rest, .. } => rest, _ => break, }; } @@ -1951,7 +1973,10 @@ where return Some(x); } match ***self.rest.as_ref()? { - Type::Record(_) | Type::Variant(_) | Type::ExtendRow { .. } => (), + Type::Record(_) + | Type::Variant(_) + | Type::ExtendRow { .. } + | Type::ExtendTypeRow { .. } => (), _ => return None, }; @@ -1966,6 +1991,7 @@ where self.fields = fields.iter_mut(); Some(rest) } + Type::ExtendTypeRow { ref mut rest, .. } => Some(rest), _ => unreachable!(), }; } @@ -2410,13 +2436,15 @@ where Self::pretty_record_like(row, printer, "{", &mut pretty_record_field, "}") } } - Type::ExtendRow { .. } => self.pretty_row("{", printer, &mut |field| { - chain![arena; - pretty_print::doc_comment(arena, field.typ.comment()), - pretty_print::ident(arena, field.name.as_ref()), - " : " - ] - }), + Type::ExtendRow { .. } | Type::ExtendTypeRow { .. } => { + self.pretty_row("{", printer, &mut |field| { + chain![arena; + pretty_print::doc_comment(arena, field.typ.comment()), + pretty_print::ident(arena, field.name.as_ref()), + " : " + ] + }) + } // This should not be displayed normally as it should only exist in `ExtendRow` // which handles `EmptyRow` explicitly Type::EmptyRow => arena.text("EmptyRow"), @@ -2429,7 +2457,11 @@ where Type::Alias(ref alias) => printer.symbol(&alias.name), }; match **typ { - Type::App(..) | Type::ExtendRow { .. } | Type::Variant(..) | Type::Function(..) => doc, + Type::App(..) + | Type::ExtendRow { .. } + | Type::ExtendTypeRow { .. } + | Type::Variant(..) + | Type::Function(..) => doc, _ => { let comment = printer.comments_before(typ.span().start()); comment.append(doc) @@ -2449,12 +2481,7 @@ where { let arena = printer.arena; - let forced_newline = match **row { - Type::ExtendRow { ref fields, .. } => { - fields.iter().any(|field| field.typ.comment().is_some()) - } - _ => false, - }; + let forced_newline = row_iter(row).any(|field| field.typ.comment().is_some()); let newline = if forced_newline { arena.newline() @@ -2470,7 +2497,7 @@ where doc = match **row { Type::EmptyRow => doc, - Type::ExtendRow { .. } => doc + Type::ExtendRow { .. } | Type::ExtendTypeRow { .. } => doc .append(top(row).pretty_row(open, printer, pretty_field)) .nest(INDENT), _ => doc @@ -2500,9 +2527,15 @@ where let mut doc = arena.nil(); let mut typ = self.typ; - let fields = match **typ { - Type::ExtendRow { ref fields, .. } => &fields[..], - _ => &[][..], + let fields = { + let mut typ = typ; + loop { + match **typ { + Type::ExtendRow { ref fields, .. } => break &fields[..], + Type::ExtendTypeRow { ref rest, .. } => typ = rest, + _ => break &[][..], + } + } }; let forced_newline = fields.iter().any(|field| field.typ.comment().is_some()); @@ -2518,89 +2551,72 @@ where let mut filtered = false; - while let Type::ExtendRow { - ref types, - ref rest, - .. - } = **typ - { - for (i, field) in types.iter().enumerate() { - let filter = printer.filter(&field.name); - if filter == Filter::Drop { - filtered = true; - continue; - } - - let f = chain![arena; - field.name.as_ref(), - arena.space(), - arena.concat(field.typ.params().iter().map(|param| { - arena.text(param.id.as_ref()).append(arena.space()) - })), - arena.text("= "), - if filter == Filter::RetainKey { - arena.text("...") - } else { - top(&field.typ.typ).pretty(printer) - }, - if i + 1 != types.len() || print_any_field { - arena.text(",") - } else { - arena.nil() - } - ] - .group(); - doc = doc.append(newline.clone()).append(f); + let types_len = type_field_iter(typ).count(); + for (i, field) in type_field_iter(typ).enumerate() { + let filter = printer.filter(&field.name); + if filter == Filter::Drop { + filtered = true; + continue; } - typ = rest; - } - if !fields.is_empty() { - typ = self.typ; + let f = chain![arena; + field.name.as_ref(), + arena.space(), + arena.concat(field.typ.params().iter().map(|param| { + arena.text(param.id.as_ref()).append(arena.space()) + })), + arena.text("= "), + if filter == Filter::RetainKey { + arena.text("...") + } else { + top(&field.typ.typ).pretty(printer) + }, + if i + 1 != types_len || print_any_field { + arena.text(",") + } else { + arena.nil() + } + ] + .group(); + doc = doc.append(newline.clone()).append(f); } - while let Type::ExtendRow { - ref fields, - ref rest, - .. - } = **typ - { - for (i, field) in fields.iter().enumerate() { - let filter = printer.filter(&field.name); - if filter == Filter::Drop { - filtered = true; - continue; - } + let mut row_iter = row_iter(typ); + for (i, field) in row_iter.by_ref().enumerate() { + let filter = printer.filter(&field.name); + if filter == Filter::Drop { + filtered = true; + continue; + } - let mut rhs = if filter == Filter::RetainKey { - arena.text("...") + let mut rhs = if filter == Filter::RetainKey { + arena.text("...") + } else { + top(&field.typ).pretty(printer) + }; + match *field.typ { + // Records handle nesting on their own + Type::Record(_) => (), + _ => rhs = rhs.nest(INDENT), + } + let f = chain![arena; + pretty_field(field), + rhs.group(), + if i + 1 != fields.len() { + arena.text(",") } else { - top(&field.typ).pretty(printer) - }; - match *field.typ { - // Records handle nesting on their own - Type::Record(_) => (), - _ => rhs = rhs.nest(INDENT), - } - let f = chain![arena; - pretty_field(field), - rhs.group(), - if i + 1 != fields.len() { - arena.text(",") - } else { - arena.nil() - } - ] - .group(); - let space_before = if i == 0 && open == "(" { arena.nil() - } else { - newline.clone() - }; - doc = doc.append(space_before).append(f); - } - typ = rest; + } + ] + .group(); + let space_before = if i == 0 && open == "(" { + arena.nil() + } else { + newline.clone() + }; + doc = doc.append(space_before).append(f); } + typ = row_iter.typ; let doc = if filtered { if let Doc::Nil = doc.1 { @@ -2707,18 +2723,23 @@ where | Type::Variant(ref typ) | Type::Effect(ref typ) => f.walk(typ), Type::ExtendRow { - ref types, ref fields, ref rest, } => { - for field in types { - f.walk(&field.typ.typ); - } for field in fields { f.walk(&field.typ); } f.walk(rest); } + Type::ExtendTypeRow { + ref types, + ref rest, + } => { + for field in types { + f.walk(&field.typ.typ); + } + f.walk(rest); + } Type::Hole | Type::Opaque | Type::Error @@ -2754,9 +2775,17 @@ where f.walk_mut(row) } Type::ExtendRow { - ref mut types, ref mut fields, ref mut rest, + } => { + for field in fields { + f.walk_mut(&mut field.typ); + } + f.walk_mut(rest); + } + Type::ExtendTypeRow { + ref mut types, + ref mut rest, } => { for field in types { if let Some(alias) = field.typ.try_get_alias_mut() { @@ -2764,9 +2793,6 @@ where f.walk_mut(field_type); } } - for field in fields { - f.walk_mut(&mut field.typ); - } f.walk_mut(rest); } Type::Hole @@ -2932,13 +2958,29 @@ macro_rules! forward_type_interner_methods { $crate::expr!(self, $($tokens)+).poly_record(types, fields, rest) } - fn extend_row( + fn extend_full_row( &mut self, types: Vec<$crate::types::Field<$id, $crate::types::Alias<$id, $typ>>>, fields: Vec<$crate::types::Field<$id, $typ>>, rest: $typ, ) -> $typ { - $crate::expr!(self, $($tokens)+).extend_row(types, fields, rest) + $crate::expr!(self, $($tokens)+).extend_full_row(types, fields, rest) + } + + fn extend_row( + &mut self, + fields: Vec<$crate::types::Field<$id, $typ>>, + rest: $typ, + ) -> $typ { + $crate::expr!(self, $($tokens)+).extend_row(fields, rest) + } + + fn extend_type_row( + &mut self, + types: Vec<$crate::types::Field<$id, $crate::types::Alias<$id, $typ>>>, + rest: $typ, + ) -> $typ { + $crate::expr!(self, $($tokens)+).extend_type_row(types, rest) } fn generic(&mut self, typ: $crate::types::Generic<$id>) -> $typ { @@ -3107,7 +3149,7 @@ pub trait TypeContext { } fn poly_variant(&mut self, fields: Vec>, rest: T) -> T { - let row = self.extend_row(Vec::new(), fields, rest); + let row = self.extend_row(fields, rest); self.intern(Type::Variant(row)) } @@ -3117,7 +3159,7 @@ pub trait TypeContext { } fn poly_effect(&mut self, fields: Vec>, rest: T) -> T { - let extend_row = self.extend_row(Vec::new(), fields, rest); + let extend_row = self.extend_row(fields, rest); self.intern(Type::Effect(extend_row)) } @@ -3138,7 +3180,6 @@ pub trait TypeContext { let empty_row = self.empty_row(); Type::Record( self.extend_row( - vec![], elems .into_iter() .enumerate() @@ -3163,24 +3204,33 @@ pub trait TypeContext { fields: Vec>, rest: T, ) -> T { - let row = self.extend_row(types, fields, rest); + let row = self.extend_full_row(types, fields, rest); self.intern(Type::Record(row)) } - fn extend_row( + fn extend_full_row( &mut self, types: Vec>>, fields: Vec>, rest: T, ) -> T { - if types.is_empty() && fields.is_empty() { + let rest = self.extend_row(fields, rest); + self.extend_type_row(types, rest) + } + + fn extend_row(&mut self, fields: Vec>, rest: T) -> T { + if fields.is_empty() { rest } else { - self.intern(Type::ExtendRow { - types, - fields, - rest, - }) + self.intern(Type::ExtendRow { fields, rest }) + } + } + + fn extend_type_row(&mut self, types: Vec>>, rest: T) -> T { + if types.is_empty() { + rest + } else { + self.intern(Type::ExtendTypeRow { types, rest }) } } @@ -3767,7 +3817,6 @@ where Type::Variant(ref row) => f.visit(row).map(|row| f.make(Type::Variant(row))), Type::Effect(ref row) => f.visit(row).map(|row| f.make(Type::Effect(row))), Type::ExtendRow { - ref types, ref fields, ref rest, } => { @@ -3777,9 +3826,17 @@ where }); let new_rest = f.visit(rest); merge(fields, new_fields, rest, new_rest, |fields, rest| { - f.make(Type::ExtendRow { + f.make(Type::ExtendRow { fields, rest }) + }) + } + Type::ExtendTypeRow { + ref types, + ref rest, + } => { + let new_rest = f.visit(rest); + new_rest.map(|rest| { + f.make(Type::ExtendTypeRow { types: types.clone(), - fields, rest, }) }) @@ -3951,32 +4008,38 @@ where kind: skolem.kind.clone(), })), Type::ExtendRow { - ref types, ref fields, ref rest, } => { - let types = types + let fields = fields .iter() .map(|field| Field { name: field.name.clone(), - typ: Alias { - _typ: translate(interner, &field.typ.as_type()), - _marker: PhantomData, - }, + typ: translate(interner, &field.typ), }) .collect(); - let fields = fields + let rest = translate(interner, rest); + + interner.extend_row(fields, rest) + } + Type::ExtendTypeRow { + ref types, + ref rest, + } => { + let types = types .iter() .map(|field| Field { name: field.name.clone(), - typ: translate(interner, &field.typ), + typ: Alias { + _typ: translate(interner, &field.typ.as_type()), + _marker: PhantomData, + }, }) .collect(); - let rest = translate(interner, rest); - interner.extend_row(types, fields, rest) + interner.extend_type_row(types, rest) } Type::Hole => interner.hole(), Type::Opaque => interner.opaque(), diff --git a/check/src/kindcheck.rs b/check/src/kindcheck.rs index ebb897a078..8fb1830fba 100644 --- a/check/src/kindcheck.rs +++ b/check/src/kindcheck.rs @@ -308,9 +308,25 @@ impl<'a> KindCheck<'a> { } Type::ExtendRow { - ref mut types, ref mut fields, ref mut rest, + } => { + for field in fields { + let kind = self.kindcheck(&mut field.typ)?; + let type_kind = self.type_kind(); + self.unify(field.typ.span(), &type_kind, kind)?; + } + + let kind = self.kindcheck(rest)?; + let row_kind = self.row_kind(); + self.unify(rest.span(), &row_kind, kind)?; + + Ok(row_kind) + } + + Type::ExtendTypeRow { + ref mut types, + ref mut rest, } => { for field in types { if let Some(alias) = field.typ.try_get_alias_mut() { @@ -328,11 +344,6 @@ impl<'a> KindCheck<'a> { })?; } } - for field in fields { - let kind = self.kindcheck(&mut field.typ)?; - let type_kind = self.type_kind(); - self.unify(field.typ.span(), &type_kind, kind)?; - } let kind = self.kindcheck(rest)?; let row_kind = self.row_kind(); @@ -416,8 +427,9 @@ impl<'a> KindCheck<'a> { } return; } - match **typ { - Type::ExtendRow { ref mut types, .. } => types.iter_mut().for_each(|field| { + + match &mut **typ { + Type::ExtendTypeRow { types, .. } => types.iter_mut().for_each(|field| { if let Some(alias) = field.typ.try_get_alias_mut() { alias .params_mut() @@ -425,18 +437,18 @@ impl<'a> KindCheck<'a> { .for_each(|var| *var = self.finalize_generic(var)) } }), - Type::Variable(ref mut var) => { + Type::Variable(var) => { let default = Some(&self.kind_cache.typ); var.kind = update_kind(&self.subs, var.kind.clone(), default); } - Type::Generic(ref mut var) => *var = self.finalize_generic(var), - Type::Alias(ref mut alias) => alias + Type::Generic(var) => *var = self.finalize_generic(var), + Type::Alias(alias) => alias .try_get_alias_mut() .expect("ICE: AstType did not provide mutable alias") .params_mut() .iter_mut() .for_each(|var| *var = self.finalize_generic(var)), - Type::Forall(ref mut params, _) => { + Type::Forall(params, _) => { for param in params { *param = self.finalize_generic(¶m); } diff --git a/check/src/rename.rs b/check/src/rename.rs index 9f45e97800..2f5a8a01c9 100644 --- a/check/src/rename.rs +++ b/check/src/rename.rs @@ -310,8 +310,8 @@ pub fn rename<'s>( } fn visit_ast_type(&mut self, s: &'c mut SpannedAstType) { - match s.value { - Type::ExtendRow { ref mut types, .. } => { + match &mut s.value { + Type::ExtendTypeRow { types, .. } => { for field in types { if let Some(alias) = field.typ.try_get_alias_mut() { if let Some(new_name) = self.rename(&field.name) { @@ -320,13 +320,13 @@ pub fn rename<'s>( } } } - Type::Projection(ref mut ids) => { + Type::Projection( ids) => { // The first id refers to a local variable so we need to rename it if let Some(new_id) = self.rename(&mut ids[0]) { ids[0] = new_id; } } - Type::Ident(ref mut id) => { + Type::Ident(id) => { if let Some(new_id) = self.rename(id) { *id = new_id; } diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index 5f5524e308..d3c9beda95 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -1831,9 +1831,8 @@ impl<'a> Typecheck<'a> { }), }, - Type::ExtendRow { + Type::ExtendTypeRow { ref types, - ref fields, ref rest, } => { let types = types @@ -1851,17 +1850,9 @@ impl<'a> Typecheck<'a> { }) .collect(); - let fields = fields - .iter() - .map(|field| Field { - name: field.name.clone(), - typ: self.translate_ast_type(&field.typ), - }) - .collect(); - let rest = self.translate_ast_type(rest); - self.extend_row(types, fields, rest) + self.extend_type_row(types, rest) } Type::Projection(ref ids) => match self.translate_projected_type(ids) { @@ -2474,9 +2465,22 @@ impl<'a> Typecheck<'a> { Type::Hole => Some(self.subs.new_var()), Type::ExtendRow { - ref types, ref fields, ref rest, + } => { + let new_fields = types::walk_move_types(fields, |field| { + self.create_unifiable_signature2(&field.typ) + .map(|typ| Field::new(field.name.clone(), typ)) + }); + let new_rest = self.create_unifiable_signature_(rest); + merge::merge(fields, new_fields, rest, new_rest, |fields, rest| { + self.intern(Type::ExtendRow { fields, rest }) + }) + } + + Type::ExtendTypeRow { + ref types, + ref rest, } => { let new_types = types::walk_move_types(types, |field| { let typ = self @@ -2492,26 +2496,12 @@ impl<'a> Typecheck<'a> { self.new_alias(field.typ.name.clone(), field.typ.params().to_owned(), typ), )) }); - let new_fields = types::walk_move_types(fields, |field| { - self.create_unifiable_signature2(&field.typ) - .map(|typ| Field::new(field.name.clone(), typ)) - }); + let new_rest = self.create_unifiable_signature_(rest); - merge::merge3( - types, - new_types, - fields, - new_fields, - rest, - new_rest, - |types, fields, rest| { - self.intern(Type::ExtendRow { - types, - fields, - rest, - }) - }, - ) + + merge::merge(types, new_types, rest, new_rest, |types, rest| { + self.intern(Type::ExtendTypeRow { types, rest }) + }) } Type::Forall(ref params, ref typ) => { diff --git a/check/src/typecheck/generalize.rs b/check/src/typecheck/generalize.rs index fe39cb6788..98faeb8313 100644 --- a/check/src/typecheck/generalize.rs +++ b/check/src/typecheck/generalize.rs @@ -333,35 +333,42 @@ fn unroll_record( ) -> Option { let mut new_types = Vec::new(); let mut new_fields = Vec::new(); - let mut current = match *typ { - Type::ExtendRow { - ref types, - ref fields, - ref rest, - } => match **rest { - Type::ExtendRow { .. } => { - new_types.extend_from_slice(types); + let mut current = match &*typ { + Type::ExtendRow { fields, rest } => match **rest { + Type::ExtendRow { .. } | Type::ExtendTypeRow { .. } => { new_fields.extend_from_slice(fields); rest } _ => return None, }, + + Type::ExtendTypeRow { types, rest } => match **rest { + Type::ExtendRow { .. } | Type::ExtendTypeRow { .. } => { + new_types.extend_from_slice(types); + rest + } + _ => return None, + }, + _ => return None, }; - while let Type::ExtendRow { - ref types, - ref fields, - ref rest, - } = **current - { - new_types.extend_from_slice(types); - new_fields.extend_from_slice(fields); - current = rest; + loop { + match &**current { + Type::ExtendRow { fields, rest } => { + new_fields.extend_from_slice(fields); + current = rest; + } + Type::ExtendTypeRow { types, rest } => { + new_types.extend_from_slice(types); + current = rest; + } + _ => break, + } } if new_types.is_empty() && new_fields.is_empty() { None } else { - Some(interner.extend_row(new_types, new_fields, current.clone())) + Some(interner.extend_full_row(new_types, new_fields, current.clone())) } } diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 03733658f7..5b661876b2 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -470,12 +470,10 @@ where ( &Type::ExtendRow { - types: ref l_types, fields: ref l_args, rest: ref l_rest, }, &Type::ExtendRow { - types: ref r_types, fields: ref r_args, rest: ref r_rest, }, @@ -487,7 +485,6 @@ where .iter() .zip(r_args) .all(|(l, r)| l.name.name_eq(&r.name)) - && l_types == r_types { let new_args = merge::merge_tuple_iter(l_args.iter().zip(r_args), |l, r| { unifier @@ -500,19 +497,9 @@ where new_args, l_rest, new_rest, - |fields, rest| subs.extend_row(l_types.clone(), fields, rest), + |fields, rest| subs.extend_row(fields, rest), )) } else if **l_rest == Type::EmptyRow && **r_rest == Type::EmptyRow { - for l_typ in expected.type_field_iter() { - if actual - .type_field_iter() - .find(|r_typ| *r_typ == l_typ) - .is_none() - { - return Err(UnifyError::TypeMismatch(expected.clone(), actual.clone())); - } - } - // HACK For non polymorphic records we need to care about field order as the // compiler assumes the order the fields occur in the type determines how // to access them @@ -572,17 +559,38 @@ where new_args, l_rest, new_rest, - |fields, rest| subs.extend_row(l_types.clone(), fields, rest), + |fields, rest| subs.extend_row(fields, rest), )) } else { unify_rows(unifier, expected, actual) } } - (&Type::ExtendRow { .. }, &Type::EmptyRow) | (&Type::EmptyRow, &Type::ExtendRow { .. }) => { - unify_rows(unifier, expected, actual) + ( + &Type::ExtendTypeRow { + types: ref l_types, + rest: ref l_rest, + }, + &Type::ExtendTypeRow { + types: ref r_types, + rest: ref r_rest, + }, + ) => { + let rest_opt = unifier.try_match(l_rest, r_rest); + if l_types == r_types { + Ok(rest_opt.map(|rest| subs.extend_type_row(l_types.clone(), rest))) + } else { + return Err(UnifyError::TypeMismatch(expected.clone(), actual.clone())); + } } + (&Type::ExtendTypeRow { .. }, &Type::ExtendRow { .. }) + | (&Type::ExtendRow { .. }, &Type::ExtendTypeRow { .. }) + | (&Type::EmptyRow, &Type::ExtendTypeRow { .. }) + | (&Type::ExtendTypeRow { .. }, &Type::EmptyRow) + | (&Type::ExtendRow { .. }, &Type::EmptyRow) + | (&Type::EmptyRow, &Type::ExtendRow { .. }) => unify_rows(unifier, expected, actual), + (&Type::Ident(ref id), &Type::Alias(ref alias)) if *id == alias.name => { Ok(Some(actual.clone())) } @@ -872,8 +880,11 @@ where } _ => { rest = subs.new_var(); - let l_rest = - subs.extend_row(types_missing_from_right, missing_from_right, rest.clone()); + let l_rest = subs.extend_full_row( + types_missing_from_right, + missing_from_right, + rest.clone(), + ); unifier.try_match(&l_rest, r_iter.current_type()); types.extend(l_rest.type_field_iter().cloned()); fields.extend(l_rest.row_iter().cloned()); @@ -906,7 +917,7 @@ where _ => { rest = subs.new_var(); let r_rest = - subs.extend_row(types_missing_from_left, missing_from_left, rest.clone()); + subs.extend_full_row(types_missing_from_left, missing_from_left, rest.clone()); unifier.try_match(l_iter.current_type(), &r_rest); types.extend(r_rest.type_field_iter().cloned()); fields.extend(r_rest.row_iter().cloned()); @@ -914,7 +925,7 @@ where } } - Ok(Some(subs.extend_row(types, fields, rest))) + Ok(Some(subs.extend_full_row(types, fields, rest))) } fn resolve_application<'t>(typ: &'t RcType, mut subs: &'t Substitution) -> Option { diff --git a/check/tests/row_polymorphism.rs b/check/tests/row_polymorphism.rs index 803e33c371..42b0082b54 100644 --- a/check/tests/row_polymorphism.rs +++ b/check/tests/row_polymorphism.rs @@ -229,7 +229,6 @@ fn row_kinds() { assert_eq!(result, Ok(Kind::row())); let mut typ = Type::extend_row( - vec![], vec![Field::new(intern("x"), Type::int())], Type::empty_row(), ); @@ -243,16 +242,11 @@ fn row_kinds_error() { let mut ident_env = MockIdentEnv::new(); let mut kindcheck = KindCheck::new(&env, &mut ident_env, KindCache::new()); - let mut typ = Type::extend_row( - vec![], - vec![Field::new(intern("x"), Type::int())], - Type::int(), - ); + let mut typ = Type::extend_row(vec![Field::new(intern("x"), Type::int())], Type::int()); let result = kindcheck.kindcheck_expected(&mut typ, &Kind::row()); assert!(result.is_err()); let mut typ = Type::extend_row( - vec![], vec![Field::new(intern("x"), Type::empty_row())], Type::empty_row(), ); diff --git a/check/tests/support/mod.rs b/check/tests/support/mod.rs index d594424359..69b98390ec 100644 --- a/check/tests/support/mod.rs +++ b/check/tests/support/mod.rs @@ -338,16 +338,11 @@ pub fn alias_variant_implicit( pub fn close_record(typ: ArcType) -> ArcType { types::walk_move_type(typ, &mut |typ: &ArcType| match **typ { Type::ExtendRow { - ref types, ref fields, ref rest, } => match **rest { Type::ExtendRow { .. } => None, - _ => Some(Type::extend_row( - types.clone(), - fields.clone(), - Type::empty_row(), - )), + _ => Some(Type::extend_row(fields.clone(), Type::empty_row())), }, _ => None, }) diff --git a/completion/src/lib.rs b/completion/src/lib.rs index cefa2b1fc7..27e8b62bdb 100644 --- a/completion/src/lib.rs +++ b/completion/src/lib.rs @@ -708,7 +708,7 @@ where fn visit_ast_type(&mut self, typ: &'a AstType) { match **typ { // ExtendRow do not have spans set properly so recurse unconditionally - Type::ExtendRow { .. } => (), + Type::ExtendRow { .. } | Type::ExtendTypeRow { .. } => (), _ if typ.span().containment(self.pos) != Ordering::Equal => return, _ => (), } diff --git a/completion/tests/support/mod.rs b/completion/tests/support/mod.rs index ceff000325..6a23ec97c9 100644 --- a/completion/tests/support/mod.rs +++ b/completion/tests/support/mod.rs @@ -230,16 +230,11 @@ where pub fn close_record(typ: ArcType) -> ArcType { types::walk_move_type(typ, &mut |typ: &ArcType| match **typ { Type::ExtendRow { - ref types, ref fields, ref rest, } => match **rest { Type::ExtendRow { .. } => None, - _ => Some(Type::extend_row( - types.clone(), - fields.clone(), - Type::empty_row(), - )), + _ => Some(Type::extend_row(fields.clone(), Type::empty_row())), }, _ => None, }) diff --git a/parser/src/grammar.lalrpop b/parser/src/grammar.lalrpop index a60fc1b2c1..d5f7b21f17 100644 --- a/parser/src/grammar.lalrpop +++ b/parser/src/grammar.lalrpop @@ -287,7 +287,7 @@ TypeTop: Either, Spanned<(Vec, Vec>, Option)>> => { let Spanned { span, value: rest } = rest; Either::Left( - pos::spanned(span, Type::Variant(pos::spanned(span, Type::ExtendRow { fields: Vec::new(), types: Vec::new(), rest }).into())).into() + pos::spanned(span, Type::Variant(pos::spanned(span, Type::ExtendRow { fields: Vec::new(), rest }).into())).into() ) }, > => Either::Right(<>), @@ -330,7 +330,7 @@ TypeBinding: Box> = { ), }) .collect(); - AstType::from(pos::spanned(span, Type::ExtendRow { types: Vec::new(), fields, rest })) + AstType::from(pos::spanned(span, Type::ExtendRow { fields, rest })) }; AstType::from(Type::forall( @@ -384,13 +384,13 @@ AtomicType_: Type> = { "(" )>> ")" => { let Spanned { span, value: rest } = rest; - Type::Variant(pos::spanned(span, Type::ExtendRow { fields: Vec::new(), types: Vec::new(), rest }).into()) + Type::Variant(pos::spanned(span, Type::ExtendRow { fields: Vec::new(), rest }).into()) }, "[" "|" > )?> "|" "]" => { let rest = rest.unwrap_or_else(Type::empty_row); - Type::Effect(Type::extend_row(vec![], fields, rest)) + Type::Effect(Type::extend_row(fields, rest)) }, "(" > ")" => @@ -402,7 +402,7 @@ AtomicType_: Type> = { "{" > )?> "}" => { let (types, fields) = row.into_iter().partition_map(|x| x); - Type::Record(Type::extend_row( + Type::Record(Type::extend_full_row( types, fields, rest.unwrap_or_else(Type::empty_row), diff --git a/vm/src/api/mod.rs b/vm/src/api/mod.rs index cacdba9f04..ab6ed93572 100644 --- a/vm/src/api/mod.rs +++ b/vm/src/api/mod.rs @@ -1,19 +1,4 @@ //! The marshalling api -use crate::base::{ - scoped_map::ScopedMap, - symbol::{Symbol, Symbols}, - types::{self, ArcType, Type}, -}; -use crate::{ - forget_lifetime, - gc::{CloneUnrooted, DataDef, GcRef, Move, Trace}, - thread::{RootedThread, ThreadInternal, VmRoot, VmRootInternal}, - types::{VmIndex, VmInt, VmTag}, - value::{ArrayDef, ArrayRepr, ClosureData, DataStruct, Value, ValueArray, ValueRepr}, - vm::{self, RootedValue, Status, Thread}, - Error, Result, Variants, -}; - use std::{ any::Any, borrow::Borrow, @@ -28,6 +13,20 @@ use std::{ result::Result as StdResult, }; +use crate::base::{ + scoped_map::ScopedMap, + symbol::{Symbol, Symbols}, + types::{self, ArcType, Field, Type}, +}; +use crate::{ + forget_lifetime, + gc::{CloneUnrooted, DataDef, GcRef, Move, Trace}, + thread::{RootedThread, ThreadInternal, VmRoot, VmRootInternal}, + types::{VmIndex, VmInt, VmTag}, + value::{ArrayDef, ArrayRepr, ClosureData, DataStruct, Value, ValueArray, ValueRepr}, + vm::{self, RootedValue, Status, Thread}, + Error, Result, Variants, +}; use futures::{Async, Future}; pub use self::{ @@ -401,14 +400,9 @@ fn insert_forall_walker( ) .map(|t| ArcType::from(Type::Variant(t))), - Type::Record(ref typ) => match **typ { - Type::ExtendRow { .. } => types::walk_move_type_opt( - typ, - &mut types::ControlVisitation(|typ: &ArcType| insert_forall(variables, typ)), - ) - .map(|typ| ArcType::from(Type::Record(typ))), - _ => None, - }, + Type::Record(ref typ) => { + insert_forall_walker_fields(variables, typ).map(|typ| ArcType::from(Type::Record(typ))) + } _ => types::walk_move_type_opt( typ, &mut types::ControlVisitation(|typ: &ArcType| insert_forall_walker(variables, typ)), @@ -416,6 +410,27 @@ fn insert_forall_walker( } } +fn insert_forall_walker_fields( + variables: &mut ScopedMap>, + typ: &ArcType, +) -> Option { + match &**typ { + Type::ExtendRow { fields, rest } => { + let new_fields = types::walk_move_types(fields, |field| { + insert_forall(variables, &field.typ).map(|typ| Field { + name: field.name.clone(), + typ, + }) + }); + let new_rest = insert_forall_walker_fields(variables, rest); + base::merge::merge(fields, new_fields, rest, new_rest, Type::extend_row) + } + Type::ExtendTypeRow { types, rest } => insert_forall_walker_fields(variables, rest) + .map(|rest| Type::extend_type_row(types.clone(), rest)), + _ => None, + } +} + /// Trait which maps a type in rust to a type in gluon pub trait VmType { /// A version of `Self` which implements `Any` allowing a `TypeId` to be retrieved diff --git a/vm/src/api/record.rs b/vm/src/api/record.rs index 0e0a2f3b70..177fbbc595 100644 --- a/vm/src/api/record.rs +++ b/vm/src/api/record.rs @@ -244,7 +244,7 @@ impl VmType for Row { T::field_types(vm, &mut type_fields); let mut fields = Vec::new(); U::field_values(vm, &mut fields); - Type::extend_row(type_fields, fields, R::make_type(vm)) + Type::extend_full_row(type_fields, fields, R::make_type(vm)) } } From c7061c7bef60b3ab3367dd06399d3bf5b8deae84 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 16 Aug 2019 07:54:57 +0200 Subject: [PATCH 15/27] perf(parser): Simplify tokenization iterators --- parser/src/layout.rs | 104 ++++++++++++++++++++-------------- parser/src/lib.rs | 130 +------------------------------------------ parser/src/token.rs | 1 + 3 files changed, 66 insertions(+), 169 deletions(-) diff --git a/parser/src/layout.rs b/parser/src/layout.rs index a59185052f..a3c85cebd4 100644 --- a/parser/src/layout.rs +++ b/parser/src/layout.rs @@ -1,6 +1,6 @@ use crate::base::pos::{self, BytePos, Column, Line, Location, Span, Spanned}; -use crate::token::{SpannedToken, Token}; +use crate::token::{self, SpannedToken, Token}; quick_error! { #[derive(Debug, PartialEq)] @@ -11,6 +11,8 @@ quick_error! { } } +type Result> = std::result::Result; + #[derive(Copy, Clone, Debug)] struct Offside { location: Location, @@ -80,16 +82,13 @@ impl Contexts { self.stack.is_empty() } - fn push(&mut self, offside: Offside) -> Result<(), Spanned> { + fn push(&mut self, offside: Offside) -> Result<()> { self.check_unindentation_limit(offside)?; self.stack.push(offside); Ok(()) } - fn check_unindentation_limit( - &mut self, - offside: Offside, - ) -> Result<(), Spanned> { + fn check_unindentation_limit(&mut self, offside: Offside) -> Result<()> { let mut skip_block = false; for other_offside in self.stack.iter().rev() { match other_offside.context { @@ -115,7 +114,7 @@ impl Contexts { return Err(pos::spanned2( offside.location.absolute, offside.location.absolute, - Error::UnindentedTooFar, + Error::UnindentedTooFar.into(), )); } Ok(()) @@ -130,7 +129,7 @@ pub struct Layout<'input, Tokens> { impl<'input, Tokens> Layout<'input, Tokens> where - Tokens: Iterator>, + Tokens: Iterator>>, { pub fn new(tokens: Tokens) -> Layout<'input, Tokens> { Layout { @@ -140,67 +139,82 @@ where } } - fn continue_block(&mut self, context: Context, token: &Token) -> bool { + fn continue_block(&mut self, context: Context, token: &Token) -> Result { let in_rec = self.indent_levels.stack.len() >= 2 && self.indent_levels.stack[self.indent_levels.stack.len() - 2].context == Context::Rec; - in_rec + Ok(in_rec && (context == Context::Attribute - || (*token != Token::Rec && self.scan_continue_block(context, token))) + || (*token != Token::Rec && self.scan_continue_block(context, token)?))) } - fn scan_continue_block(&mut self, context: Context, first_token: &Token) -> bool { + fn scan_continue_block(&mut self, context: Context, first_token: &Token) -> Result { let expected_token = match context { Context::Let => Token::Let, Context::Type => Token::Type, - _ => return false, + _ => return Ok(false), }; let mut in_attribute = false; for i in 0.. { let peek_token = if i == 0 { Some(first_token) } else { - self.peek_token(i - 1).map(|t| &t.value) + self.peek_token(i - 1)?.map(|t| &t.value) }; if peek_token == Some(&expected_token) { - return true; + return Ok(true); } match peek_token { Some(peek_token) => match peek_token { Token::AttributeOpen => in_attribute = true, Token::DocComment(..) => (), Token::RBracket => in_attribute = false, - _ if !in_attribute => return false, + _ if !in_attribute => return Ok(false), _ => (), }, - None => return false, + None => return Ok(false), } } - false + Ok(false) } - fn peek_token(&mut self, n: usize) -> Option<&SpannedToken<'input>> { + fn peek_token(&mut self, n: usize) -> Result>> { for _ in self.unprocessed_tokens.len()..=n { let token = match self.tokens.next() { - Some(token) => token, - None => return None, + Some(token) => token.map_err(|err| { + pos::spanned2( + err.span.start().absolute, + err.span.end().absolute, + err.value.into(), + ) + })?, + None => return Ok(None), }; self.unprocessed_tokens.insert(0, token); } - self.unprocessed_tokens.first() + Ok(self.unprocessed_tokens.first()) } - fn next_token(&mut self) -> SpannedToken<'input> { - self.unprocessed_tokens.pop().unwrap_or_else(|| { - self.tokens.next().unwrap_or_else(|| { - // Blegh - let location = Location { - line: Line::from(0), - column: Column::from(1), - absolute: BytePos::from(1), - }; - pos::spanned2(location, location, Token::EOF) - }) + fn next_token(&mut self) -> Result> { + self.unprocessed_tokens.pop().map(Ok).unwrap_or_else(|| { + self.tokens + .next() + .unwrap_or_else(|| { + // Blegh + let location = Location { + line: Line::from(0), + column: Column::from(1), + absolute: BytePos::from(1), + }; + Ok(pos::spanned2(location, location, Token::EOF)) + }) + .map_err(|err| { + pos::spanned2( + err.span.start().absolute, + err.span.end().absolute, + err.value.into(), + ) + }) }) } @@ -214,8 +228,8 @@ where pos::spanned(span, layout_token) } - fn scan_for_next_block(&mut self, context: Context) -> Result<(), Spanned> { - let next = self.next_token(); + fn scan_for_next_block(&mut self, context: Context) -> Result<()> { + let next = self.next_token()?; let span = next.span; self.unprocessed_tokens.push(next); @@ -259,10 +273,10 @@ where self.indent_levels.push(Offside::new(span.start(), context)) } - fn layout_next_token(&mut self) -> Result, Spanned> { + fn layout_next_token(&mut self) -> Result> { use std::cmp::Ordering; - let mut token = self.next_token(); + let mut token = self.next_token()?; if token.value == Token::EOF { let mut start = token.span.start(); start.column = Column::from(0); @@ -446,7 +460,7 @@ where | (Context::Type, Ordering::Less) if token.value != Token::RBrace => { - if !self.continue_block(offside.context, &token.value) { + if !self.continue_block(offside.context, &token.value)? { if token.value == Token::EOF { self.indent_levels.pop(); continue; @@ -551,7 +565,7 @@ where (&Token::With, _) => self.scan_for_next_block(Context::MatchClause)?, (&Token::Else, _) => { - let next = self.next_token(); + let next = self.next_token()?; // Need to allow "else if" expressions so avoid inserting a block for those // cases (A block would be inserted at column 5 and we would then get // unindentation errors on the branches) @@ -607,16 +621,22 @@ fn token_closes_context(token: &Token, context: Context) -> bool { impl<'input, Tokens> Iterator for Layout<'input, Tokens> where - Tokens: Iterator>, + Tokens: Iterator>>, { - type Item = Result, Spanned>; + type Item = Result<(BytePos, Token<'input>, BytePos)>; fn next(&mut self) -> Option { match self.layout_next_token() { Ok(Spanned { value: Token::EOF, .. }) => None, - token => Some(token), + token => Some(token.map(|token| { + ( + token.span.start().absolute, + token.value.into(), + token.span.end().absolute, + ) + })), } } } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index f008d194a0..e31c18b38e 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -21,7 +21,7 @@ extern crate quick_error; #[macro_use] extern crate pretty_assertions; -use std::{cell::RefCell, fmt, hash::Hash, sync::Arc}; +use std::{fmt, hash::Hash, sync::Arc}; use crate::base::{ ast::{AstType, Do, Expr, IdentEnv, SpannedExpr, SpannedPattern, TypedIdent, ValueBinding}, @@ -233,76 +233,6 @@ impl Error { } } -/// An iterator which forwards only the `Ok` values. If an `Err` is found the iterator returns -/// `None` and the error can be retrieved using the `result` method. -struct ResultOkIter { - iter: I, - error: Option, -} - -impl ResultOkIter { - fn new(iter: I) -> ResultOkIter { - ResultOkIter { - iter: iter, - error: None, - } - } - - fn result(&mut self, value: T) -> Result { - match self.error.take() { - Some(err) => Err(err), - None => Ok(value), - } - } -} - -impl Iterator for ResultOkIter -where - I: Iterator>, - E: ::std::fmt::Debug, -{ - type Item = T; - - fn next(&mut self) -> Option { - match self.iter.next() { - Some(Ok(t)) => Some(t), - Some(Err(err)) => { - self.error = Some(err); - None - } - None => None, - } - } -} - -/// An iterator which can be shared -struct SharedIter<'a, I: 'a> { - iter: &'a RefCell, -} - -impl<'a, I> Clone for SharedIter<'a, I> { - fn clone(&self) -> SharedIter<'a, I> { - SharedIter { iter: self.iter } - } -} - -impl<'a, I> SharedIter<'a, I> { - fn new(iter: &'a RefCell) -> SharedIter<'a, I> { - SharedIter { iter } - } -} - -impl<'a, I> Iterator for SharedIter<'a, I> -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.borrow_mut().next() - } -} - pub enum FieldPattern { Type(Spanned, Option), Value(Spanned, Option>), @@ -323,30 +253,6 @@ type MutIdentEnv<'env, Id> = &'env mut dyn IdentEnv; type ErrorEnv<'err, 'input> = &'err mut Errors>; pub type ParseErrors = Errors>; -macro_rules! layout { - ($result_ok_iter:ident, $input:expr) => {{ - let tokenizer = Tokenizer::new($input); - $result_ok_iter = RefCell::new(ResultOkIter::new(tokenizer)); - - Layout::new(SharedIter::new(&$result_ok_iter)).map(|token| { - // Return the tokenizer error if one exists - $result_ok_iter.borrow_mut().result(()).map_err(|err| { - pos::spanned2( - err.span.start().absolute, - err.span.end().absolute, - err.value.into(), - ) - })?; - let token = token.map_err(|err| pos::spanned(err.span, err.value.into()))?; - debug!("Lex {:?}", token.value); - Ok(( - token.span.start().absolute, - token.value, - token.span.end().absolute, - )) - }) - }}; -} pub trait ParserSource { fn src(&self) -> &str; @@ -397,28 +303,13 @@ where Id: Clone + AsRef, S: ?Sized + ParserSource, { - let result_ok_iter; - let layout = layout!(result_ok_iter, input); + let layout = Layout::new(Tokenizer::new(input)); let mut parse_errors = Errors::new(); let result = grammar::TopExprParser::new().parse(&input, type_cache, symbols, &mut parse_errors, layout); - // If there is a tokenizer error it may still exist in the result iterator wrapper. - // If that is the case we return that error instead of the unexpected EOF error that lalrpop - // emitted - if let Err(err) = result_ok_iter.borrow_mut().result(()) { - parse_errors.pop(); // Remove the EOF error - parse_errors.push(lalrpop_util::ParseError::User { - error: pos::spanned2( - err.span.start().absolute, - err.span.end().absolute, - err.value.into(), - ), - }); - } - match result { Ok(expr) => { if parse_errors.has_errors() { @@ -456,8 +347,7 @@ where Id: Clone + Eq + Hash + AsRef + ::std::fmt::Debug, S: ?Sized + ParserSource, { - let result_ok_iter; - let layout = layout!(result_ok_iter, input); + let layout = Layout::new(Tokenizer::new(input)); let mut parse_errors = Errors::new(); @@ -471,20 +361,6 @@ where layout, ); - // If there is a tokenizer error it may still exist in the result iterator wrapper. - // If that is the case we return that error instead of the unexpected EOF error that lalrpop - // emitted - if let Err(err) = result_ok_iter.borrow_mut().result(()) { - parse_errors.pop(); // Remove the EOF error - parse_errors.push(lalrpop_util::ParseError::User { - error: pos::spanned2( - err.span.start().absolute, - err.span.end().absolute, - err.value.into(), - ), - }); - } - match result { Ok(repl_line) => { let repl_line = repl_line.map(|b| *b); diff --git a/parser/src/token.rs b/parser/src/token.rs index d6d54da7e8..177a8e4f32 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -125,6 +125,7 @@ impl<'input> fmt::Display for Token<'input> { pub type SpannedToken<'input> = Spanned, Location>; pub type SpError = Spanned; +pub type Result = std::result::Result; quick_error! { #[derive(Clone, Debug, PartialEq, Eq)] From 3016f251b35f01da81ab8ea51e2ea8f62c1ac870 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 16 Aug 2019 09:35:23 +0200 Subject: [PATCH 16/27] perf(parser): Shrink the Token type and remove it's need to Drop --- base/src/metadata.rs | 4 +- parser/src/grammar.lalrpop | 8 ++-- parser/src/token.rs | 90 ++++++++++++++++++++++++++++---------- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/base/src/metadata.rs b/base/src/metadata.rs index 9e8e4e4149..f43d184d6f 100644 --- a/base/src/metadata.rs +++ b/base/src/metadata.rs @@ -30,9 +30,9 @@ pub enum CommentType { #[derive(Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "serde_derive", derive(Deserialize, Serialize))] -pub struct Comment { +pub struct Comment { pub typ: CommentType, - pub content: String, + pub content: S, } #[derive(Clone, Debug, Default, Eq, PartialEq)] diff --git a/parser/src/grammar.lalrpop b/parser/src/grammar.lalrpop index d5f7b21f17..9795213791 100644 --- a/parser/src/grammar.lalrpop +++ b/parser/src/grammar.lalrpop @@ -12,7 +12,7 @@ use crate::base::{ use std::str::FromStr; use crate::{ReplLine, Variant, new_ident}; -use crate::token::Token; +use crate::token::{Token, StringLiteral}; use crate::ordered_float::NotNan; use crate::{Error, ErrorEnv, FieldExpr, FieldPattern, MutIdentEnv}; @@ -28,12 +28,12 @@ extern { "shebang line" => Token::ShebangLine(<&'input str>), "identifier" => Token::Identifier(<&'input str>), "operator" => Token::Operator(<&'input str>), - "string literal" => Token::StringLiteral(), + "string literal" => Token::StringLiteral(>), "char literal" => Token::CharLiteral(), "int literal" => Token::IntLiteral(), "byte literal" => Token::ByteLiteral(), "float literal" => Token::FloatLiteral(), - "documentation comment" => Token::DocComment(), + "documentation comment" => Token::DocComment(>), "rec" => Token::Rec, "else" => Token::Else, @@ -549,7 +549,7 @@ Pattern = { // Expressions Literal: Literal = { - "string literal" => Literal::String(<>), + "string literal" => Literal::String(<>.unescape()), "char literal" => Literal::Char(<>), "int literal" => Literal::Int(<>), "byte literal" => Literal::Byte(<>), diff --git a/parser/src/token.rs b/parser/src/token.rs index 177a8e4f32..71e6486e74 100644 --- a/parser/src/token.rs +++ b/parser/src/token.rs @@ -16,12 +16,12 @@ pub enum Token<'input> { Identifier(&'input str), Operator(&'input str), - StringLiteral(String), + StringLiteral(StringLiteral<'input>), CharLiteral(char), IntLiteral(i64), ByteLiteral(u8), FloatLiteral(f64), - DocComment(Comment), + DocComment(Comment<&'input str>), Rec, Else, @@ -122,6 +122,43 @@ impl<'input> fmt::Display for Token<'input> { } } +#[derive(Clone, PartialEq, Debug)] +pub enum StringLiteral<'input> { + Escaped(&'input str), + Raw(&'input str), +} + +impl StringLiteral<'_> { + pub fn unescape(&self) -> String { + match self { + StringLiteral::Escaped(s) => unescape_string_literal(s), + StringLiteral::Raw(s) => s.to_string(), + } + } +} + +fn unescape_string_literal(mut s: &str) -> String { + let mut string = String::new(); + while let Some(i) = s.bytes().position(|b| b == b'\\') { + let c = match s.as_bytes()[i + 1] { + b'\'' => '\'', + b'"' => '"', + b'\\' => '\\', + b'/' => '/', + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + _ => panic!("Invalid escape"), + }; + string.push_str(&s[..i]); + string.push(c); + s = &s[i + 2..]; + } + string.push_str(s); + + string +} + pub type SpannedToken<'input> = Spanned, Location>; pub type SpError = Spanned; @@ -332,7 +369,7 @@ impl<'input> Tokenizer<'input> { let skip = if comment.starts_with("/// ") { 4 } else { 3 }; let doc = Token::DocComment(Comment { typ: CommentType::Line, - content: comment[skip..].to_string(), + content: &comment[skip..], }); Some(pos::spanned2(start, end, doc)) } else { @@ -354,7 +391,7 @@ impl<'input> Tokenizer<'input> { // FIXME: whitespace alignment let doc = Token::DocComment(Comment { typ: CommentType::Block, - content: comment[3..].trim().to_string(), + content: &comment[3..].trim(), }); return Ok(Some(pos::spanned2(start, end, doc))); } else { @@ -409,19 +446,23 @@ impl<'input> Tokenizer<'input> { } fn string_literal(&mut self, start: Location) -> Result, SpError> { - let mut string = String::new(); + let content_start = self.next_loc(); loop { - let content_start = self.next_loc(); - let (_end, s) = self.take_until(content_start, |b| b == b'"' || b == b'\\'); - string.push_str(s); + let scan_start = self.next_loc(); + self.take_until(scan_start, |b| b == b'"' || b == b'\\'); match self.bump() { Some((_, b'\\')) => { - string.push(self.escape_code()? as char); + self.escape_code()?; } Some((_, b'"')) => { let end = self.next_loc(); - let token = Token::StringLiteral(string); + let mut content_end = end; + content_end.absolute.0 -= 1; + + let token = Token::StringLiteral(StringLiteral::Escaped( + self.slice(content_start, content_end), + )); return Ok(pos::spanned2(start, end, token)); } _ => break, @@ -457,9 +498,9 @@ impl<'input> Tokenizer<'input> { let end = self.next_loc(); let mut content_end = end; content_end.absolute.0 -= delimiters + 1; - let string = self.slice(content_start, content_end).into(); + let string = self.slice(content_start, content_end); - let token = Token::StringLiteral(string); + let token = Token::StringLiteral(StringLiteral::Raw(string)); return Ok(pos::spanned2(start, end, token)); } } @@ -713,7 +754,7 @@ mod test { use codespan::{ByteOffset, ColumnOffset}; use super::*; - use super::{error, Tokenizer}; + use super::{error, StringLiteral, Tokenizer}; use crate::token::Token; use crate::token::Token::*; @@ -846,24 +887,29 @@ mod test { #[test] fn string_literals() { test( - r#"foo "bar\"\n" baz "" "\t""#, + r#"foo "bar\"\n" baz "" "\t" "\"\"""#, vec![ (r#"~~~ "#, Identifier("foo")), ( r#" ~~~~~~~~~ "#, - StringLiteral("bar\"\n".to_string()), + Token::StringLiteral(StringLiteral::Escaped("bar\\\"\\n")), ), (r#" ~~~ "#, Identifier("baz")), ( r#" ~~ "#, - StringLiteral("".to_string()), + Token::StringLiteral(StringLiteral::Escaped("")), ), ( r#" ~~~~"#, - StringLiteral("\t".to_string()), + Token::StringLiteral(StringLiteral::Escaped("\\t")), + ), + ( + r#" ~~~~~~"#, + Token::StringLiteral(StringLiteral::Escaped(r#"\"\""#)), ), ], ); + assert_eq!(StringLiteral::Escaped(r#"\"\""#).unescape(), r#""""#); } #[test] @@ -874,12 +920,12 @@ mod test { (r####"~~~ "####, Identifier("foo")), ( r#" ~~~~~~~~~~ "#, - StringLiteral("bar\" ".to_string()), + Token::StringLiteral(StringLiteral::Raw("bar\" ")), ), (r####" ~~~ "####, Identifier("baz")), ( r#" ~~~~~~~ "#, - StringLiteral("".to_string()), + Token::StringLiteral(StringLiteral::Raw("")), ), ], ); @@ -1124,7 +1170,7 @@ mod test { r#" ~~~~~~~~~~~~~~~~"#, DocComment(Comment { typ: CommentType::Line, - content: "hellooo/// hi".to_string(), + content: "hellooo/// hi", }), ), ], @@ -1141,7 +1187,7 @@ mod test { r#" ~~~~~~~~~~~~~~~~~"#, DocComment(Comment { typ: CommentType::Line, - content: "hellooo/// hi".to_string(), + content: "hellooo/// hi", }), ), ], @@ -1162,7 +1208,7 @@ mod test { " \n ~~~~~~~~~~~~~~~~~", DocComment(Comment { typ: CommentType::Line, - content: "hellooo/// hi".to_string(), + content: "hellooo/// hi", }), ), ], From a709c7120ff151d368128cf7c0a89d667c91015c Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Fri, 16 Aug 2019 09:48:14 +0200 Subject: [PATCH 17/27] perf: Only do one hash lookup when creating a Symbol --- Cargo.lock | 31 ++++++++++++++-------- base/Cargo.toml | 1 + base/src/symbol.rs | 64 +++++++++++++++++++++++++++------------------- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1bcea06938..80c471c016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,10 +7,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ahash" -version = "0.2.6" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "const-random 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -374,20 +374,20 @@ dependencies = [ [[package]] name = "const-random" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "const-random-macro 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "const-random-macro" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -897,6 +897,7 @@ dependencies = [ "collect-mac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1140,11 +1141,20 @@ name = "hashbrown" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ahash 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ahash 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hashbrown" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ahash 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "heck" version = "0.3.1" @@ -3120,7 +3130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" -"checksum ahash 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "283982a5d5cb2dd3416ea06b703279f289decbfea9bf8da7983a19e7968ca013" +"checksum ahash 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e96e12a0287c75063711e04484e9b140d4a59ec074d3fe5f0b1cc90e0e992665" "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" @@ -3163,8 +3173,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum codespan-reporting 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2ae73f6c4b3803dc2a0fe08ed1ce40e8f3f94ecc8394a82e0696bbc86d4e4fc3" "checksum collect-mac 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f168712e49987bd2f51cb855c4585999e12b1a0abdff60fea4b81b41f2010264" "checksum compiletest_rs 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "f40ecc9332b68270998995c00f8051ee856121764a0d3230e64c9efd059d27b6" -"checksum const-random 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c430a6c6170b741ced6334950903f1cfd27385ce0bf80fb22561b636e09e089" -"checksum const-random-macro 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf38483934c41305eaf335e39d77f1803ed553ffbf4533746a51a14a41158c5" +"checksum const-random 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7b641a8c9867e341f3295564203b1c250eb8ce6cb6126e007941f78c4d2ed7fe" +"checksum const-random-macro 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c750ec12b83377637110d5a57f5ae08e895b06c4b16e2bdbf1a94ef717428c59" "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" "checksum cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" "checksum cookie_store 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46750b3f362965f197996c4448e4a0935e791bf7d6631bfce9ee0af3d24c919c" @@ -3220,6 +3230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum handlebars 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df044dd42cdb7e32f28557b661406fc0f2494be75199779998810dbc35030e0d" "checksum hashbrown 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b072077810586b86f17e055b13721b7585266575d552146a0e9b74b7c6da0a5e" +"checksum hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bcea5b597dd98e6d1f1ec171744cc5dee1a30d1c23c5b98e3cf9d4fbdf8a526" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c213fa6a618dc1da552f54f85cba74b05d8e883c92ec4e89067736938084c26e" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" diff --git a/base/Cargo.toml b/base/Cargo.toml index 461abd13fa..86a5e99ee7 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -17,6 +17,7 @@ travis-ci = { repository = "gluon-lang/gluon" } [dependencies] bitflags = "1" +hashbrown = "0.6" log = "0.4" quick-error = "1.0.0" fnv = "1.0.3" diff --git a/base/src/symbol.rs b/base/src/symbol.rs index 985ebbe6b5..422d0dfdb3 100644 --- a/base/src/symbol.rs +++ b/base/src/symbol.rs @@ -2,12 +2,11 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt; -use std::hash::{Hash, Hasher}; +use std::hash::{BuildHasher, BuildHasherDefault, Hash, Hasher}; use std::ops::Deref; use std::sync::Arc; use crate::ast::{DisplayEnv, IdentEnv}; -use crate::fnv::FnvMap; // FIXME Don't have a double indirection (Arc + String) /// A symbol uniquely identifies something regardless of its name and which module it originated @@ -479,35 +478,17 @@ impl<'a> From<&'a Name> for NameBuf { /// Used to make identifiers within a single module point to the same symbol #[derive(Debug, Default)] pub struct Symbols { - indexes: FnvMap, Symbol>, + indexes: + hashbrown::HashMap, Symbol, BuildHasherDefault>, } impl Symbols { pub fn new() -> Symbols { Symbols { - indexes: FnvMap::default(), + indexes: Default::default(), } } - fn make_symbol(&mut self, global: bool, location: Option<(u32, u32)>, name: NameBuf) -> Symbol { - // `name` is fixed in memory and the key lives as long as `s` this is safe - let key = unsafe { &*(&*name as *const Name) }; - let s = Symbol(Arc::new(SymbolData { - global, - location, - name, - })); - self.indexes.insert( - SymbolData { - global, - location, - name: key, - }, - s.clone(), - ); - s - } - pub fn simple_symbol(&mut self, name: N) -> Symbol where N: Into + AsRef, @@ -529,10 +510,39 @@ impl Symbols { location: name.location, name: name.name.as_ref(), }; - if let Some(symbol) = self.indexes.get(&name_ref) { - return symbol.clone(); - } - self.make_symbol(name.global, name.location, name.name.into()) + + let mut hasher = self.indexes.hasher().build_hasher(); + name_ref.hash(&mut hasher); + let hash = hasher.finish(); + + self.indexes + .raw_entry_mut() + .from_hash(hash, |key| *key == name_ref) + .or_insert_with(|| { + let SymbolData { + global, + location, + name, + } = name; + let name: NameBuf = name.into(); + + let key = unsafe { &*(&*name as *const Name) }; + let s = Symbol(Arc::new(SymbolData { + global, + location, + name, + })); + ( + SymbolData { + global, + location, + name: key, + }, + s, + ) + }) + .1 + .clone() } pub fn contains_name(&mut self, name: N) -> bool From 78967bda60586e49bfb1633e82023d2861af7475 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 00:06:20 +0200 Subject: [PATCH 18/27] refactor(check): Simplify union --- check/src/substitution.rs | 40 +++++++++++---------------------------- check/src/unify.rs | 8 ++++---- check/src/unify_type.rs | 4 ---- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/check/src/substitution.rs b/check/src/substitution.rs index 0f5e268e15..ef9422b477 100644 --- a/check/src/substitution.rs +++ b/check/src/substitution.rs @@ -142,10 +142,6 @@ pub trait Substitutable: Sized { fn contains_variables(&self) -> bool { true } - - fn on_union(&self) -> Option<&Self> { - None - } } pub fn occurs(typ: &T, subs: &Substitution, var: u32) -> bool @@ -459,7 +455,7 @@ impl Substitution { impl Substitution { /// Takes `id` and updates the substitution to say that it should have the same type as `typ` - pub fn union(&self, variable: &T, typ: &T) -> Result, Error> + pub fn union(&self, variable: &T, typ: &T) -> Result<(), Error> where T::Variable: Clone, T: fmt::Display, @@ -467,42 +463,28 @@ impl Substitution { assert!(variable.get_id().is_some(), "Expected a variable"); let id = variable.get_id().unwrap(); - let resolved_type = typ.on_union(); - let typ = resolved_type.unwrap_or(typ); // Nothing needs to be done if both are the same variable already (also prevents the occurs // check from failing) if typ.get_var().map_or(false, |other| other.get_id() == id) { - return Ok(None); + return Ok(()); } if occurs(typ, self, id) { return Err(Error::Occurs(variable.clone(), typ.clone())); } - { - let id_type = self.find_type_for_var(id); - let other_type = self.real(typ); - if id_type.map_or(false, |x| x == other_type) - || other_type.get_var().map(|y| y.get_id()) == Some(id) - { - return Ok(None); + match typ.get_var().map(|v| v.get_id()) { + Some(other_id) if variable.get_var().is_some() => { + self.union.borrow_mut().union(id, other_id); + self.update_level(id.get_id(), other_id); + self.update_level(other_id, id.get_id()); } - } - { - let typ = resolved_type.unwrap_or(typ); - match typ.get_var().map(|v| v.get_id()) { - Some(other_id) if variable.get_var().is_some() => { - self.union.borrow_mut().union(id, other_id); + _ => { + if let Some(other_id) = typ.get_id() { self.update_level(id.get_id(), other_id); - self.update_level(other_id, id.get_id()); - } - _ => { - if let Some(other_id) = typ.get_id() { - self.update_level(id.get_id(), other_id); - } - self.insert(id.get_id(), typ.clone()); } + self.insert(id.get_id(), typ.clone()); } } - Ok(resolved_type.cloned()) + Ok(()) } } diff --git a/check/src/unify.rs b/check/src/unify.rs index 87c96b291f..b9865ffa3f 100644 --- a/check/src/unify.rs +++ b/check/src/unify.rs @@ -208,13 +208,13 @@ where // unified with whatever the other type is match (l.get_var(), r.get_var()) { (_, Some(_)) => { - let replacement = subs.union(r, l)?; - debug!("Union {} <> {}", l, replacement.as_ref().unwrap_or(r)); + debug!("Union {} <> {}", l, r); + subs.union(r, l)?; Ok(None) } (Some(_), _) => { - let replacement = subs.union(l, r)?; - debug!("Union {} <> {}", replacement.as_ref().unwrap_or(l), r); + debug!("Union {} <> {}", l, r); + subs.union(l, r)?; Ok(None) } (None, None) => { diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 5b661876b2..745532eaa6 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -299,10 +299,6 @@ impl Substitutable for RcType { self.instantiate_generics(&mut subs, &mut FnvMap::default()) } - fn on_union(&self) -> Option<&Self> { - None - } - fn contains_variables(&self) -> bool { self.flags().contains(Flags::HAS_VARIABLES) } From 03e7c3b4b22e5abd53b2af64f09849ba0ca43b32 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 00:21:34 +0200 Subject: [PATCH 19/27] =?UTF-8?q?perf(check):=20Remove=20some=20branches?= =?UTF-8?q?=20in=20the=20occurs=20check=C2=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- check/src/substitution.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/check/src/substitution.rs b/check/src/substitution.rs index ef9422b477..0c49c94f6e 100644 --- a/check/src/substitution.rs +++ b/check/src/substitution.rs @@ -153,22 +153,26 @@ where var: u32, subs: &'a Substitution, } - impl<'a, 't, T> Walker<'t, T> for Occurs<'a, T> + impl<'t, T> Walker<'t, T> for Occurs<'t, T> where T: Substitutable, { - fn walk(&mut self, typ: &'t T) { - if !typ.contains_variables() || self.occurs { + fn walk(&mut self, mut typ: &'t T) { + if !typ.contains_variables() { return; } - let typ = self.subs.real(typ); if let Some(other) = typ.get_var() { - if self.var.get_id() == other.get_id() { - self.occurs = true; - typ.traverse(self); - return; + let other_id = other.get_id(); + if let Some(real_type) = self.subs.find_type_for_var(other_id) { + typ = real_type; + } else { + if self.var.get_id() == other_id { + self.occurs = true; + typ.traverse(self); + return; + } + self.subs.update_level(self.var, other.get_id()); } - self.subs.update_level(self.var, other.get_id()); } typ.traverse(self); } From 5e4efe37386f4d937fa8afa8b8d06b4aed7c738e Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 08:03:35 +0200 Subject: [PATCH 20/27] perf(check): No need to lookup the type again before querying the level --- check/src/substitution.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/check/src/substitution.rs b/check/src/substitution.rs index 0c49c94f6e..1627a749d1 100644 --- a/check/src/substitution.rs +++ b/check/src/substitution.rs @@ -413,21 +413,13 @@ where } /// Updates the level of `other` to be the minimum level value of `var` and `other` - pub fn update_level(&self, var: u32, other: u32) { + fn update_level(&self, var: u32, other: u32) { let level = ::std::cmp::min(self.get_level(var), self.get_level(other)); let mut union = self.union.borrow_mut(); union.union_value(other, Level(level)); } - pub fn set_level(&self, var: u32, level: u32) { - let mut union = self.union.borrow_mut(); - union.union_value(var, Level(level)); - } - - pub fn get_level(&self, mut var: u32) -> u32 { - if let Some(v) = self.find_type_for_var(var) { - var = v.get_var().map_or(var, |v| v.get_id()); - } + pub fn get_level(&self, var: u32) -> u32 { let mut union = self.union.borrow_mut(); union.union_value(var, Level(var)); union.probe_value(var).0 From f3d4203a44074467e40240afc2cbf5547403efcc Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 08:09:29 +0200 Subject: [PATCH 21/27] perf(check): Remove redundant operations in union --- check/src/substitution.rs | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/check/src/substitution.rs b/check/src/substitution.rs index 1627a749d1..fd0438c787 100644 --- a/check/src/substitution.rs +++ b/check/src/substitution.rs @@ -216,7 +216,7 @@ pub struct Level(u32); impl ena::unify::UnifyValue for Level { type Error = ena::unify::NoError; fn unify_values(l: &Self, r: &Self) -> Result { - Ok(if l < r { l.clone() } else { r.clone() }) + Ok(*l.min(r)) } } @@ -334,8 +334,8 @@ where pub fn clear_from(&mut self, level: u32) { self.union = RefCell::new(ena::unify::InPlaceUnificationTable::new()); let mut u = self.union.borrow_mut(); - for _ in 0..level { - u.new_key(Level(::std::u32::MAX)); + for var in 0..level { + u.new_key(Level(var)); } let variable_cache = self.variable_cache.get_mut(); @@ -367,11 +367,7 @@ where F: FnOnce(u32) -> T, { let var_id = self.variables.len() as u32; - let id = self - .union - .borrow_mut() - .new_key(Level(::std::u32::MAX)) - .index; + let id = self.union.borrow_mut().new_key(Level(var_id)).index; assert!(id as usize == self.variables.len()); debug!("New var {}", self.variables.len()); @@ -414,14 +410,13 @@ where /// Updates the level of `other` to be the minimum level value of `var` and `other` fn update_level(&self, var: u32, other: u32) { - let level = ::std::cmp::min(self.get_level(var), self.get_level(other)); let mut union = self.union.borrow_mut(); - union.union_value(other, Level(level)); + let level = union.probe_value(var).min(union.probe_value(other)); + union.union_value(other, level); } pub fn get_level(&self, var: u32) -> u32 { let mut union = self.union.borrow_mut(); - union.union_value(var, Level(var)); union.probe_value(var).0 } @@ -459,23 +454,13 @@ impl Substitution { assert!(variable.get_id().is_some(), "Expected a variable"); let id = variable.get_id().unwrap(); - // Nothing needs to be done if both are the same variable already (also prevents the occurs - // check from failing) - if typ.get_var().map_or(false, |other| other.get_id() == id) { - return Ok(()); - } - if occurs(typ, self, id) { - return Err(Error::Occurs(variable.clone(), typ.clone())); - } - match typ.get_var().map(|v| v.get_id()) { - Some(other_id) if variable.get_var().is_some() => { - self.union.borrow_mut().union(id, other_id); - self.update_level(id.get_id(), other_id); - self.update_level(other_id, id.get_id()); + match typ.get_var() { + Some(other_var) if variable.get_var().is_some() => { + self.union.borrow_mut().union(id, other_var.get_id()); } _ => { - if let Some(other_id) = typ.get_id() { - self.update_level(id.get_id(), other_id); + if occurs(typ, self, id) { + return Err(Error::Occurs(variable.clone(), typ.clone())); } self.insert(id.get_id(), typ.clone()); } From 1244143838ed56fcc7d257ef6f9288d38754f60d Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 08:16:39 +0200 Subject: [PATCH 22/27] perf(check): Use RefCell::get_mut when possible --- check/src/substitution.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check/src/substitution.rs b/check/src/substitution.rs index fd0438c787..25652292e8 100644 --- a/check/src/substitution.rs +++ b/check/src/substitution.rs @@ -298,7 +298,7 @@ where pub fn replace(&mut self, var: u32, t: T) { debug_assert!(t.get_id() != Some(var)); - self.types_undo.borrow_mut().push(var); + self.types_undo.get_mut().push(var); self.types.insert(var as usize, t.into()); } @@ -333,7 +333,7 @@ where /// Assumes that no variables unified with anything (but variables < level may exist) pub fn clear_from(&mut self, level: u32) { self.union = RefCell::new(ena::unify::InPlaceUnificationTable::new()); - let mut u = self.union.borrow_mut(); + let u = self.union.get_mut(); for var in 0..level { u.new_key(Level(var)); } From fa6ab6609577345b4a6bcbdc66551d6ab7268996 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 09:36:35 +0200 Subject: [PATCH 23/27] refactor: Construct the more specific type during unification --- check/src/unify.rs | 2 +- check/src/unify_type.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/check/src/unify.rs b/check/src/unify.rs index b9865ffa3f..0534eff84b 100644 --- a/check/src/unify.rs +++ b/check/src/unify.rs @@ -215,7 +215,7 @@ where (Some(_), _) => { debug!("Union {} <> {}", l, r); subs.union(l, r)?; - Ok(None) + Ok(Some(r.clone())) } (None, None) => { // Both sides are concrete types, the only way they can be equal is if diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 745532eaa6..622ace0243 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -1383,7 +1383,7 @@ impl<'a, 'e> Unifier, RcType> for UnifierState<'a, Subsume<'e>> { (Type::Variable(_), _) => { debug!("Union merge {} <> {}", l, r); subs.union(l, r)?; - Ok(None) + Ok(Some(r.clone())) } (Type::Skolem(_), _) if self.state.refinement => { From 6ce443a9552f5cb7daa42a84a5a10d63362f5ab7 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 12:44:17 +0200 Subject: [PATCH 24/27] Fix skolem unification ordering --- check/src/unify_type.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/check/src/unify_type.rs b/check/src/unify_type.rs index 622ace0243..dcad21d4c2 100644 --- a/check/src/unify_type.rs +++ b/check/src/unify_type.rs @@ -1373,7 +1373,7 @@ impl<'a, 'e> Unifier, RcType> for UnifierState<'a, Subsume<'e>> { // `l` and `r` must have the same type, if one is a variable that variable is // unified with whatever the other type is match (&**l, &**r) { - (&Type::Hole, _) => Ok(Some(r.clone())), + (Type::Hole, _) => Ok(Some(r.clone())), (_, Type::Variable(_)) => { debug!("Union merge {} <> {}", l, r); @@ -1386,9 +1386,12 @@ impl<'a, 'e> Unifier, RcType> for UnifierState<'a, Subsume<'e>> { Ok(Some(r.clone())) } - (Type::Skolem(_), _) if self.state.refinement => { + (Type::Skolem(l_skolem), _) if self.state.refinement => { debug!("Skolem union {} <> {}", l, r); - subs.union(l, r)?; + match &**r { + Type::Skolem(r_skolem) if r_skolem.id > l_skolem.id => subs.union(r, l)?, + _ => subs.union(l, r)?, + } Ok(None) } From 7bf25e892f87db8606d6fdd91c39d4093ea8d70f Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 21:17:33 +0200 Subject: [PATCH 25/27] refactor(check): Replace the TailCall trampoline with a loop --- check/src/typecheck.rs | 288 ++++++++++++++++++----------------------- 1 file changed, 128 insertions(+), 160 deletions(-) diff --git a/check/src/typecheck.rs b/check/src/typecheck.rs index d3c9beda95..413ac33808 100644 --- a/check/src/typecheck.rs +++ b/check/src/typecheck.rs @@ -125,16 +125,6 @@ impl Environment<'_> { } } -/// Type returned from the main typecheck function to make sure that nested `type` and `let` -/// expressions dont overflow the stack -enum TailCall { - Type(ModType), - TypeImplicit(ModType, Vec>), - /// Returned from typechecking a `let` or `type` expresion to indicate that the expression body - /// should be typechecked now. - TailCall, -} - /// Struct which provides methods to typecheck expressions. pub struct Typecheck<'a> { pub(crate) environment: Environment<'a>, @@ -515,11 +505,11 @@ impl<'a> Typecheck<'a> { expr.span, ModType::rigid(&expected_type), |self_, expected_type| { - self_.typecheck_opt_(true, expr, Some(ModType::rigid(&expected_type))) + self_.typecheck_bindings(true, expr, Some(ModType::rigid(&expected_type))) }, ) } else { - self.typecheck_opt_(true, expr, None) + self.typecheck_bindings(true, expr, None) }; { @@ -607,117 +597,51 @@ impl<'a> Typecheck<'a> { fn typecheck_opt( &mut self, expr: &mut SpannedExpr, - expected_type: Option, - ) -> ModType { - self.typecheck_opt_(false, expr, expected_type) - } - - fn typecheck_opt_( - &mut self, - mut top_level: bool, - mut expr: &mut SpannedExpr, mut expected_type: Option, ) -> ModType { - fn moving(t: T) -> T { - t - } - // How many scopes that have been entered in this "tailcall" loop - let mut scope_count = 0; let returned_type; - let level = self.subs.var_id(); - loop { - match self.typecheck_(expr, &mut expected_type) { - Ok(tailcall) => { - match tailcall { - TailCall::TailCall => { - // Call typecheck_ again with the next expression - expr = match moving(expr).value { - Expr::LetBindings(ref mut binds, ref mut new_expr) => { - if top_level { - self.generalize_and_clear_subs(level, binds); - } - new_expr - } - Expr::TypeBindings(_, ref mut new_expr) => new_expr, - Expr::Do(Do { - body: ref mut new_expr, - .. - }) => { - top_level = false; - new_expr - } - _ => ice!("Only Let and Type expressions can tailcall"), - }; - scope_count += 1; - } - TailCall::TypeImplicit(typ, args) => { - if !args.is_empty() { - match expr.value { - Expr::App { - ref mut implicit_args, - .. - } => { - if implicit_args.is_empty() { - *implicit_args = args; - } else { - implicit_args.extend(args); - } - } - _ => { - let dummy = Expr::Literal(Literal::Int(0)); - let func = mem::replace(&mut expr.value, dummy); - expr.value = Expr::App { - func: Box::new(pos::spanned(expr.span, func)), - implicit_args: args, - args: vec![], - } - } - } - } - returned_type = match expected_type { - Some(expected_type) => ModType::new( - expected_type.modifier, - self.subsumes_expr( - expr.span, - &typ, - expected_type.concrete.clone(), - expr, - ), - ), - None => typ, - }; - break; + match self.typecheck_(expr, &mut expected_type) { + Ok((typ, args)) => { + if !args.is_empty() { + match expr.value { + Expr::App { + ref mut implicit_args, + .. + } => { + if implicit_args.is_empty() { + *implicit_args = args; + } else { + implicit_args.extend(args); + } } - TailCall::Type(typ) => { - returned_type = match expected_type { - Some(expected_type) => ModType::new( - expected_type.modifier, - self.subsumes_expr( - expr.span, - &typ, - expected_type.concrete.clone(), - expr, - ), - ), - None => typ, - }; - break; + _ => { + let dummy = Expr::Literal(Literal::Int(0)); + let func = mem::replace(&mut expr.value, dummy); + expr.value = Expr::App { + func: Box::new(pos::spanned(expr.span, func)), + implicit_args: args, + args: vec![], + } } } } - Err(err) => { - returned_type = ModType::wobbly(self.subs.error()); - self.errors.push(Spanned { - span: expr_check_span(expr), - value: err.into(), - }); - break; - } + + returned_type = match expected_type { + Some(expected_type) => ModType::new( + expected_type.modifier, + self.subsumes_expr(expr.span, &typ, expected_type.concrete.clone(), expr), + ), + None => typ, + }; + } + Err(err) => { + returned_type = ModType::wobbly(self.subs.error()); + self.errors.push(Spanned { + span: expr_check_span(expr), + value: err.into(), + }); } - } - for _ in 0..scope_count { - self.exit_scope(); } returned_type } @@ -728,9 +652,9 @@ impl<'a> Typecheck<'a> { &mut self, expr: &mut SpannedExpr, expected_type: &mut Option, - ) -> TcResult { + ) -> TcResult<(ModType, Vec>)> { if let Some(result) = self.check_macro(expr) { - return result; + return Ok((result?, Vec::new())); } match expr.value { Expr::Ident(ref mut id) => { @@ -742,15 +666,18 @@ impl<'a> Typecheck<'a> { &mut expected_type.take().map(|t| t.concrete), ); id.typ = self.subs.bind_arc(&typ); - Ok(TailCall::TypeImplicit(ModType::new(modifier, typ), args)) - } - Expr::Literal(ref lit) => Ok(TailCall::Type(ModType::rigid(match *lit { - Literal::Int(_) => self.subs.int(), - Literal::Byte(_) => self.subs.byte(), - Literal::Float(_) => self.subs.float(), - Literal::String(_) => self.subs.string(), - Literal::Char(_) => self.subs.char(), - }))), + Ok((ModType::new(modifier, typ), args)) + } + Expr::Literal(ref lit) => Ok(( + ModType::rigid(match *lit { + Literal::Int(_) => self.subs.int(), + Literal::Byte(_) => self.subs.byte(), + Literal::Float(_) => self.subs.float(), + Literal::String(_) => self.subs.string(), + Literal::Char(_) => self.subs.char(), + }), + Vec::new(), + )), Expr::App { ref mut func, ref mut implicit_args, @@ -774,7 +701,7 @@ impl<'a> Typecheck<'a> { let false_type = self.instantiate_generics(&false_type); self.unify(&true_type, false_type) - .map(|t| TailCall::Type(ModType::new(modifier, t))) + .map(|t| (ModType::new(modifier, t), Vec::new())) } Expr::Infix { ref mut lhs, @@ -846,7 +773,7 @@ impl<'a> Typecheck<'a> { } }; *typ = self.subs.bind_arc(&new_type); - Ok(TailCall::Type(new_type)) + Ok((new_type, Vec::new())) } Expr::Match(ref mut expr, ref mut alts) => { let mut scrutinee_type = self.infer_expr(&mut **expr); @@ -927,12 +854,16 @@ impl<'a> Typecheck<'a> { expr_type = Some(alt_type); } - expr_type.ok_or(TypeError::EmptyCase).map(TailCall::Type) - } - Expr::LetBindings(ref mut bindings, _) => { - self.typecheck_bindings(bindings)?; - Ok(TailCall::TailCall) + expr_type + .ok_or(TypeError::EmptyCase) + .map(|typ| (typ, Vec::new())) } + + Expr::TypeBindings(..) | Expr::LetBindings(..) => Ok(( + self.typecheck_bindings(false, expr, expected_type.take()), + Vec::new(), + )), + Expr::Projection(ref mut expr, ref field_id, ref mut ast_field_typ) => { let mut expr_typ = self.infer_expr(&mut **expr); let modifier = expr_typ.modifier; @@ -975,12 +906,9 @@ impl<'a> Typecheck<'a> { } }; *ast_field_typ = self.subs.bind_arc(&new_ast_field_type); - Ok(TailCall::TypeImplicit( - ModType::new(modifier, new_ast_field_type), - implicit_args, - )) + Ok((ModType::new(modifier, new_ast_field_type), implicit_args)) } - Type::Error => Ok(TailCall::Type(ModType::new(modifier, record.clone()))), + Type::Error => Ok((ModType::new(modifier, record.clone()), Vec::new())), _ => Err(TypeError::InvalidProjection(record)), } } @@ -998,7 +926,7 @@ impl<'a> Typecheck<'a> { self.typecheck(expr, ModType::wobbly(&expected_element_type)); } - Ok(TailCall::Type(ModType::wobbly(array_type))) + Ok((ModType::wobbly(array_type), Vec::new())) } Expr::Lambda(ref mut lambda) => { let level = self.subs.var_id(); @@ -1020,11 +948,7 @@ impl<'a> Typecheck<'a> { self.generalize_type(level, &mut typ, expr.span); lambda.id.typ = self.subs.bind_arc(&typ); - Ok(TailCall::Type(typ.clone())) - } - Expr::TypeBindings(ref mut bindings, ref expr) => { - self.typecheck_type_bindings(bindings, expr); - Ok(TailCall::TailCall) + Ok((typ.clone(), Vec::new())) } Expr::Record { ref mut typ, @@ -1202,16 +1126,14 @@ impl<'a> Typecheck<'a> { let new_type = self.subs.record(new_types, new_fields); *typ = self.subs.bind_arc(&new_type); - Ok(TailCall::Type(ModType::new(modifier, new_type))) + Ok((ModType::new(modifier, new_type), Vec::new())) } Expr::Block(ref mut exprs) => { let (last, exprs) = exprs.split_last_mut().expect("Expr in block"); for expr in exprs { self.infer_expr(expr); } - Ok(TailCall::Type( - self.typecheck_opt(last, expected_type.take()), - )) + Ok((self.typecheck_opt(last, expected_type.take()), Vec::new())) } Expr::Do(Do { ref mut id, @@ -1291,7 +1213,7 @@ impl<'a> Typecheck<'a> { let ret = self.unify_span(body.span, &ret, body_type.concrete.clone()); - Ok(TailCall::Type(ModType::wobbly(ret))) + Ok((ModType::wobbly(ret), Vec::new())) } Expr::MacroExpansion { ref mut replacement, @@ -1306,11 +1228,14 @@ impl<'a> Typecheck<'a> { self.typecheck_(expr, &mut Some(ModType::rigid(&typ))) } - Expr::Error(ref typ) => Ok(TailCall::Type(ModType::wobbly( - typ.as_ref() - .map(|typ| self.translate_arc_type(typ)) - .unwrap_or_else(|| self.subs.new_var()), - ))), + Expr::Error(ref typ) => Ok(( + ModType::wobbly( + typ.as_ref() + .map(|typ| self.translate_arc_type(typ)) + .unwrap_or_else(|| self.subs.new_var()), + ), + Vec::new(), + )), } } @@ -1320,12 +1245,13 @@ impl<'a> Typecheck<'a> { func_type: ModType, implicit_args: &mut Vec>, args: I, - ) -> TcResult + ) -> TcResult<(ModType, Vec>)> where I: IntoIterator>, I::IntoIter: ExactSizeIterator, { self.typecheck_application_(span, func_type, implicit_args, &mut args.into_iter()) + .map(|typ| (typ, Vec::new())) } fn typecheck_application_<'e>( @@ -1334,7 +1260,7 @@ impl<'a> Typecheck<'a> { func_type: ModType, implicit_args: &mut Vec>, args: &mut dyn ExactSizeIterator>, - ) -> TcResult { + ) -> TcResult { fn attach_extra_argument_help(self_: &mut Typecheck, actual: u32, f: F) -> R where F: FnOnce(&mut Typecheck) -> R, @@ -1456,7 +1382,7 @@ impl<'a> Typecheck<'a> { }) .walk(&self.subs.zonk(&original_func_type)); - Ok(TailCall::Type(ModType::new(modifier, func_type))) + Ok(ModType::new(modifier, func_type)) } fn typecheck_lambda( @@ -1866,7 +1792,50 @@ impl<'a> Typecheck<'a> { } } - fn typecheck_bindings(&mut self, bindings: &mut ValueBindings) -> TcResult<()> { + fn typecheck_bindings( + &mut self, + top_level: bool, + mut expr: &mut SpannedExpr, + expected_type: Option, + ) -> ModType { + let mut scope_count = 0; + + let level = self.subs.var_id(); + + loop { + match expr.value { + Expr::LetBindings(ref mut bindings, ref mut body) => { + self.typecheck_let_bindings(bindings); + + scope_count += 1; + + if top_level { + self.generalize_and_clear_subs(level, bindings); + } + + expr = body + } + Expr::TypeBindings(ref mut bindings, ref mut body) => { + self.typecheck_type_bindings(bindings, body); + + scope_count += 1; + + expr = body; + } + _ => { + let typ = self.typecheck_opt(expr, expected_type); + + for _ in 0..scope_count { + self.exit_scope(); + } + + return typ; + } + } + } + } + + fn typecheck_let_bindings(&mut self, bindings: &mut ValueBindings) { self.enter_scope(); self.environment.skolem_variables.enter_scope(); self.environment.type_variables.enter_scope(); @@ -2014,7 +1983,6 @@ impl<'a> Typecheck<'a> { debug!("Typecheck `in`"); self.environment.type_variables.exit_scope(); self.environment.skolem_variables.exit_scope(); - Ok(()) } fn typecheck_type_bindings( @@ -2970,7 +2938,7 @@ impl<'a> Typecheck<'a> { }) } - fn check_macro(&mut self, expr: &mut SpannedExpr) -> Option> { + fn check_macro(&mut self, expr: &mut SpannedExpr) -> Option> { let (replacement, typ) = match expr.value { Expr::App { ref mut func, @@ -3110,7 +3078,7 @@ impl<'a> Typecheck<'a> { *expr = Expr::annotated(replacement, self.subs.bind_arc(&typ)); - Some(Ok(TailCall::Type(ModType::wobbly(typ)))) + Some(Ok(ModType::wobbly(typ))) } } From 045f2edc8545a547ff0ac8345fb1f2861c2fbda2 Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Sat, 17 Aug 2019 21:17:43 +0200 Subject: [PATCH 26/27] Loop instead of recursion --- src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 01d0ee5b74..4a8f8532c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -677,12 +677,15 @@ impl Compiler { let original_expr = mem::replace(expr, prelude_expr); // Replace the 0 in the prelude with the actual expression - fn assign_last_body(l: &mut SpannedExpr, original_expr: SpannedExpr) { - match l.value { - ast::Expr::LetBindings(_, ref mut e) => { - assign_last_body(e, original_expr); + fn assign_last_body(mut l: &mut SpannedExpr, original_expr: SpannedExpr) { + loop { + match l.value { + ast::Expr::LetBindings(_, ref mut e) => l = e, + _ => { + *l = original_expr; + return; + } } - _ => *l = original_expr, } } assign_last_body(expr, original_expr); From 32b1f82d4cc9848e5f97a1dfbbcdc51a7a4f999a Mon Sep 17 00:00:00 2001 From: Markus Westerlind Date: Mon, 2 Sep 2019 12:17:33 +0200 Subject: [PATCH 27/27] Fix non 64-bit builds --- base/src/types/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/base/src/types/mod.rs b/base/src/types/mod.rs index 5356d59016..5862e5895c 100644 --- a/base/src/types/mod.rs +++ b/base/src/types/mod.rs @@ -851,6 +851,7 @@ pub enum Type> { Skolem(#[cfg_attr(feature = "serde_derive", serde(state))] Skolem), } +#[cfg(target_pointer_width = "64")] // Safeguard against accidentally growing Type as it is a core type const _: [(); 8 * 6] = [(); std::mem::size_of::>()];