From 5e2ccdbfd58964254c088d6ca51c6e8d540ae68f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 9 May 2024 19:33:16 -0300 Subject: [PATCH 001/203] Import params pending def --- crates/compiler/can/src/def.rs | 66 +++++++++++++++++++++++++--- crates/compiler/module/src/symbol.rs | 21 +++++++-- 2 files changed, 76 insertions(+), 11 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index dec4f04b64e..81dbe7d013c 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -164,6 +164,11 @@ enum PendingValueDef<'a> { &'a Loc>, &'a Loc>, ), + /// Module params from an import + ImportParams( + Loc, + ast::Collection<'a, Loc>>>, + ), /// Ingested file IngestedFile( Loc, @@ -178,6 +183,7 @@ impl PendingValueDef<'_> { PendingValueDef::AnnotationOnly(_, loc_pattern, _) => loc_pattern, PendingValueDef::Body(loc_pattern, _) => loc_pattern, PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern, + PendingValueDef::ImportParams(loc_pattern, _) => loc_pattern, PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern, } } @@ -1111,8 +1117,21 @@ fn canonicalize_value_defs<'a>( PendingValue::ExpectFx(pending_expect) => { pending_expect_fx.push(pending_expect); } - PendingValue::ModuleImport(introduced_import) => { - imports_introduced.push(introduced_import); + PendingValue::ModuleImport { + module_id, + region, + exposed_symbols, + params, + } => { + imports_introduced.push(IntroducedImport { + module_id, + region, + exposed_symbols, + }); + + if let Some((loc_pattern, params)) = params { + pending_value_defs.push(PendingValueDef::ImportParams(loc_pattern, params)); + } } PendingValue::InvalidIngestedFile => { /* skip */ } PendingValue::ImportNameConflict => { /* skip */ } @@ -2354,6 +2373,9 @@ fn canonicalize_pending_value_def<'a>( None, ) } + ImportParams(_, _) => { + todo!("agus: handle import params def") + } IngestedFile(loc_pattern, opt_loc_ann, path_literal) => { let relative_path = if let ast::StrLiteral::PlainLine(ingested_path) = path_literal.value { @@ -2895,7 +2917,15 @@ enum PendingValue<'a> { Dbg(PendingExpectOrDbg<'a>), Expect(PendingExpectOrDbg<'a>), ExpectFx(PendingExpectOrDbg<'a>), - ModuleImport(IntroducedImport), + ModuleImport { + module_id: ModuleId, + region: Region, + exposed_symbols: Vec<(Symbol, Region)>, + params: Option<( + Loc, + ast::Collection<'a, Loc>>>, + )>, + }, SignatureDefMismatch, InvalidIngestedFile, ImportNameConflict, @@ -3056,10 +3086,25 @@ fn to_pending_value_def<'a>( None => module_name.clone(), }; + let params = if let Some(params) = module_import.params { + let name_str = name_with_alias.as_str(); + + // todo(agus): params specific loc + match scope.introduce_str(format!("#{name_str}").as_str(), region) { + Ok(sym) => Some((sym, params)), + Err(_) => { + // Ignore conflict, it will be handled right after + None + } + } + } else { + None + }; + if let Err(existing_import) = scope .modules - .insert(name_with_alias.clone(), module_id, region) + .insert(name_with_alias.clone(), module_id, params.map(|(sym, _)| sym), region) { env.problems.push(Problem::ImportNameConflict { name: name_with_alias, @@ -3070,7 +3115,7 @@ fn to_pending_value_def<'a>( }); return PendingValue::ImportNameConflict; - } + }; let exposed_names = module_import .exposed @@ -3125,11 +3170,18 @@ fn to_pending_value_def<'a>( } - PendingValue::ModuleImport(IntroducedImport { + let params = + params.map(|(sym, params)| { + // todo(agus): params specific loc + (Loc::at(region, Pattern::Identifier(sym)), params.params) + }); + + PendingValue::ModuleImport { module_id, region, exposed_symbols, - }) + params, + } } IngestedFileImport(ingested_file) => { let loc_name = ingested_file.name.item; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index e100dd50b28..7765dcf7c05 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -625,7 +625,8 @@ impl ModuleIds { #[derive(Debug, Clone)] pub struct ScopeModules { modules: VecMap, - sources: VecMap, + sources: VecMap, // todo(agus): why not Vec? + params: Vec>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -652,6 +653,7 @@ impl ScopeModules { &mut self, module_name: ModuleName, module_id: ModuleId, + params_symbol: Option, region: Region, ) -> Result<(), ScopeModuleSource> { if let Some(existing_module_id) = self.modules.get(&module_name) { @@ -665,22 +667,26 @@ impl ScopeModules { self.modules.insert(module_name, module_id); self.sources .insert(module_id, ScopeModuleSource::Import(region)); + self.params.push(params_symbol); Ok(()) } pub fn len(&self) -> usize { debug_assert_eq!(self.modules.len(), self.sources.len()); + debug_assert_eq!(self.modules.len(), self.params.len()); self.modules.len() } pub fn is_empty(&self) -> bool { debug_assert_eq!(self.modules.is_empty(), self.sources.is_empty()); + debug_assert_eq!(self.modules.is_empty(), self.params.is_empty()); self.modules.is_empty() } pub fn truncate(&mut self, len: usize) { self.modules.truncate(len); self.sources.truncate(len); + self.params.truncate(len); } } @@ -1031,22 +1037,29 @@ macro_rules! define_builtins { let mut modules = VecMap::with_capacity(capacity); let mut sources = VecMap::with_capacity(capacity); + let mut params = Vec::with_capacity(capacity); - modules.insert(home_name, home_id); - sources.insert(home_id, ScopeModuleSource::Current); + // todo(agus): Use insert + + if !home_id.is_builtin() { + modules.insert(home_name, home_id); + sources.insert(home_id, ScopeModuleSource::Current); + params.push(None); + } let mut insert_both = |id: ModuleId, name_str: &'static str| { let name: ModuleName = name_str.into(); modules.insert(name, id); sources.insert(id, ScopeModuleSource::Builtin); + params.push(None); }; $( insert_both(ModuleId::$module_const, $module_name); )+ - ScopeModules { modules, sources } + ScopeModules { modules, sources, params } } } From 96e2d32fa6306cd1f38f8579e3b609c102ae2a62 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 9 May 2024 19:52:59 -0300 Subject: [PATCH 002/203] Canonicalize import params record --- crates/compiler/can/src/def.rs | 24 +++++++++++-- crates/compiler/can/src/expr.rs | 63 +++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 81dbe7d013c..4222baa0949 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -10,6 +10,7 @@ use crate::annotation::IntroducedVariables; use crate::annotation::OwnedNamedOrAble; use crate::derive; use crate::env::Env; +use crate::expr::canonicalize_record; use crate::expr::get_lookup_symbols; use crate::expr::AnnotatedMark; use crate::expr::ClosureData; @@ -2373,8 +2374,27 @@ fn canonicalize_pending_value_def<'a>( None, ) } - ImportParams(_, _) => { - todo!("agus: handle import params def") + ImportParams(loc_pattern, params) => { + let (expr, can_output) = + canonicalize_record(env, var_store, scope, loc_pattern.region, params); + + output.union(can_output); + + let loc_expr = Loc::at(loc_pattern.region, expr); + + let def = single_can_def( + loc_pattern, + loc_expr, + var_store.fresh(), + None, + SendMap::default(), + ); + + DefOutput { + output, + references: DefReferences::Value(References::new()), + def, + } } IngestedFile(loc_pattern, opt_loc_ann, path_literal) => { let relative_path = diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 70e8f4dec31..edd92aa8fa9 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -632,33 +632,8 @@ pub fn canonicalize_expr<'a>( (answer, Output::default()) } ast::Expr::Record(fields) => { - if fields.is_empty() { - (EmptyRecord, Output::default()) - } else { - match canonicalize_fields(env, var_store, scope, region, fields.items) { - Ok((can_fields, output)) => ( - Record { - record_var: var_store.fresh(), - fields: can_fields, - }, - output, - ), - Err(CanonicalizeRecordProblem::InvalidOptionalValue { - field_name, - field_region, - record_region, - }) => ( - Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue { - field_name, - field_region, - record_region, - }), - Output::default(), - ), - } - } + canonicalize_record(env, var_store, scope, region, *fields) } - ast::Expr::RecordUpdate { fields, update: loc_update, @@ -1424,6 +1399,42 @@ pub fn canonicalize_expr<'a>( ) } +pub fn canonicalize_record<'a>( + env: &mut Env<'a>, + var_store: &mut VarStore, + scope: &mut Scope, + region: Region, + fields: ast::Collection<'a, Loc>>>, +) -> (Expr, Output) { + use Expr::*; + + if fields.is_empty() { + (EmptyRecord, Output::default()) + } else { + match canonicalize_fields(env, var_store, scope, region, fields.items) { + Ok((can_fields, output)) => ( + Record { + record_var: var_store.fresh(), + fields: can_fields, + }, + output, + ), + Err(CanonicalizeRecordProblem::InvalidOptionalValue { + field_name, + field_region, + record_region, + }) => ( + Expr::RuntimeError(roc_problem::can::RuntimeError::InvalidOptionalValue { + field_name, + field_region, + record_region, + }), + Output::default(), + ), + } + } +} + pub fn canonicalize_closure<'a>( env: &mut Env<'a>, var_store: &mut VarStore, From 9d26adb2289eace3eb8d833766605afd5d2f85b4 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 9 May 2024 21:56:41 -0300 Subject: [PATCH 003/203] Qualified lookups return params --- crates/compiler/can/src/annotation.rs | 4 +- crates/compiler/can/src/def.rs | 32 ++++++++------ crates/compiler/can/src/env.rs | 41 ++++++++++-------- crates/compiler/can/src/expr.rs | 14 +++--- crates/compiler/collections/src/vec_map.rs | 13 ++++++ crates/compiler/module/src/symbol.rs | 50 ++++++++++++++++++++-- 6 files changed, 112 insertions(+), 42 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index bd817c21cef..5c6f458a879 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -3,7 +3,7 @@ use crate::procedure::{QualifiedReference, References}; use crate::scope::{PendingAbilitiesInScope, Scope}; use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet}; use roc_module::ident::{Ident, Lowercase, TagName}; -use roc_module::symbol::Symbol; +use roc_module::symbol::{LookedupSymbol, Symbol}; use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader}; use roc_problem::can::ShadowKind; use roc_region::all::{Loc, Region}; @@ -398,7 +398,7 @@ pub(crate) fn make_apply_symbol( } } else { match env.qualified_lookup(scope, module_name, ident, region) { - Ok(symbol) => { + Ok(LookedupSymbol { symbol, params: _ }) => { references.insert_type_lookup(symbol, QualifiedReference::Qualified); Ok(symbol) } diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 4222baa0949..02f26511a39 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -32,6 +32,7 @@ use roc_module::ident::Lowercase; use roc_module::ident::ModuleName; use roc_module::ident::QualifiedModuleName; use roc_module::symbol::IdentId; +use roc_module::symbol::LookedupSymbol; use roc_module::symbol::ModuleId; use roc_module::symbol::Symbol; use roc_parse::ast; @@ -512,19 +513,21 @@ fn canonicalize_claimed_ability_impl<'a>( let label_str = label.value; let region = label.region; - let member_symbol = - match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) { - Ok(symbol) => symbol, - Err(_) => { - env.problem(Problem::NotAnAbilityMember { - ability, - name: label_str.to_owned(), - region, - }); + let LookedupSymbol { + symbol: member_symbol, + params: _, + } = match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) { + Ok(symbol) => symbol, + Err(_) => { + env.problem(Problem::NotAnAbilityMember { + ability, + name: label_str.to_owned(), + region, + }); - return Err(()); - } - }; + return Err(()); + } + }; // There are two options for how the implementation symbol is defined. // @@ -604,7 +607,10 @@ fn canonicalize_claimed_ability_impl<'a>( }; let impl_region = value.region; - let member_symbol = match env.qualified_lookup_with_module_id( + let LookedupSymbol { + symbol: member_symbol, + params: _, + } = match env.qualified_lookup_with_module_id( scope, ability_home, label.value, diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index e2f6d2ee23d..880e1c6753c 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -5,7 +5,10 @@ use crate::scope::Scope; use bumpalo::Bump; use roc_collections::{MutMap, VecSet}; use roc_module::ident::{Ident, ModuleName}; -use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol}; +use roc_module::symbol::{ + IdentIdsByModule, LookedupModule, LookedupSymbol, ModuleId, PQModuleName, PackageModuleIds, + Symbol, +}; use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Loc, Region}; @@ -74,7 +77,7 @@ impl<'a> Env<'a> { module_name_str: &str, ident: &str, region: Region, - ) -> Result { + ) -> Result { debug_assert!( !module_name_str.is_empty(), "Called env.qualified_lookup with an unqualified ident: {ident:?}" @@ -82,8 +85,10 @@ impl<'a> Env<'a> { let module_name = ModuleName::from(module_name_str); - match scope.modules.get_id(&module_name) { - Some(module_id) => self.qualified_lookup_help(scope, module_id, ident, region), + match scope.modules.lookup(&module_name) { + Some(lookedup_module) => { + self.qualified_lookup_help(scope, lookedup_module, ident, region) + } None => Err(RuntimeError::ModuleNotImported { module_name: module_name.clone(), imported_modules: scope @@ -106,11 +111,11 @@ impl<'a> Env<'a> { module_id: ModuleId, ident: &str, region: Region, - ) -> Result { - if !scope.modules.has_id(module_id) { - Err(self.module_exists_but_not_imported(scope, module_id, region)) + ) -> Result { + if let Some(module) = scope.modules.lookup_by_id(&module_id) { + self.qualified_lookup_help(scope, module, ident, region) } else { - self.qualified_lookup_help(scope, module_id, ident, region) + Err(self.module_exists_but_not_imported(scope, module_id, region)) } } @@ -118,18 +123,18 @@ impl<'a> Env<'a> { fn qualified_lookup_help( &mut self, scope: &Scope, - module_id: ModuleId, + module: LookedupModule, ident: &str, region: Region, - ) -> Result { + ) -> Result { let is_type_name = ident.starts_with(|c: char| c.is_uppercase()); // You can do qualified lookups on your own module, e.g. // if I'm in the Foo module, I can do a `Foo.bar` lookup. - if module_id == self.home { + if module.id == self.home { match scope.locals.ident_ids.get_id(ident) { Some(ident_id) => { - let symbol = Symbol::new(module_id, ident_id); + let symbol = Symbol::new(module.id, ident_id); if is_type_name { self.qualified_type_lookups.insert(symbol); @@ -137,7 +142,7 @@ impl<'a> Env<'a> { self.qualified_value_lookups.insert(symbol); } - Ok(symbol) + Ok(LookedupSymbol::no_params(symbol)) } None => { let error = RuntimeError::LookupNotInScope { @@ -157,10 +162,10 @@ impl<'a> Env<'a> { } } } else { - match self.dep_idents.get(&module_id) { + match self.dep_idents.get(&module.id) { Some(exposed_ids) => match exposed_ids.get_id(ident) { Some(ident_id) => { - let symbol = Symbol::new(module_id, ident_id); + let symbol = Symbol::new(module.id, ident_id); if is_type_name { self.qualified_type_lookups.insert(symbol); @@ -168,12 +173,12 @@ impl<'a> Env<'a> { self.qualified_value_lookups.insert(symbol); } - Ok(symbol) + Ok(module.into_symbol(symbol)) } None => Err(RuntimeError::ValueNotExposed { module_name: self .qualified_module_ids - .get_name(module_id) + .get_name(module.id) .expect("Module ID known, but not in the module IDs somehow") .as_inner() .clone(), @@ -182,7 +187,7 @@ impl<'a> Env<'a> { exposed_values: exposed_ids.exposed_values(), }), }, - _ => Err(self.module_exists_but_not_imported(scope, module_id, region)), + _ => Err(self.module_exists_but_not_imported(scope, module.id, region)), } } } diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index edd92aa8fa9..3ce8269914a 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1884,19 +1884,23 @@ fn canonicalize_var_lookup( // Since module_name was nonempty, this is a qualified var. // Look it up in the env! match env.qualified_lookup(scope, module_name, ident, region) { - Ok(symbol) => { + Ok(lookedup_symbol) => { output .references - .insert_value_lookup(symbol, QualifiedReference::Qualified); + .insert_value_lookup(lookedup_symbol.symbol, QualifiedReference::Qualified); - if scope.abilities_store.is_ability_member_name(symbol) { + if scope + .abilities_store + .is_ability_member_name(lookedup_symbol.symbol) + { + // todo(agus): params for abilities? AbilityMember( - symbol, + lookedup_symbol.symbol, Some(scope.abilities_store.fresh_specialization_id()), var_store.fresh(), ) } else { - Var(symbol, var_store.fresh()) + Var(lookedup_symbol, var_store.fresh()) } } Err(problem) => { diff --git a/crates/compiler/collections/src/vec_map.rs b/crates/compiler/collections/src/vec_map.rs index 69ebd1b592d..9fea256b56a 100644 --- a/crates/compiler/collections/src/vec_map.rs +++ b/crates/compiler/collections/src/vec_map.rs @@ -84,6 +84,13 @@ impl VecMap { } } + pub fn get_with_index(&self, key: &K) -> Option<(usize, &V)> { + self.keys + .iter() + .position(|x| x == key) + .map(|index| (index, &self.values[index])) + } + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { match self.keys.iter().position(|x| x == key) { None => None, @@ -152,6 +159,12 @@ impl VecMap { } } +impl VecMap { + pub fn get_index_by_value(&self, value: &V) -> Option { + self.values.iter().position(|x| x == value) + } +} + impl Extend<(K, V)> for VecMap { #[inline(always)] fn extend>(&mut self, iter: T) { diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 7765dcf7c05..a3a31b69c24 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -622,6 +622,22 @@ impl ModuleIds { } } +#[derive(Debug, Clone)] +pub struct LookedupSymbol { + pub symbol: Symbol, + pub params: Option, +} + +impl LookedupSymbol { + pub fn new(symbol: Symbol, params: Option) -> Self { + Self { symbol, params } + } + + pub fn no_params(symbol: Symbol) -> Self { + Self::new(symbol, None) + } +} + #[derive(Debug, Clone)] pub struct ScopeModules { modules: VecMap, @@ -629,6 +645,22 @@ pub struct ScopeModules { params: Vec>, } +pub struct LookedupModule { + pub id: ModuleId, + pub params: Option, +} + +impl LookedupModule { + pub fn into_symbol(&self, symbol: Symbol) -> LookedupSymbol { + debug_assert_eq!(symbol.module_id(), self.id); + + LookedupSymbol { + symbol, + params: self.params, + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ScopeModuleSource { Builtin, @@ -637,12 +669,22 @@ pub enum ScopeModuleSource { } impl ScopeModules { - pub fn get_id(&self, module_name: &ModuleName) -> Option { - self.modules.get(module_name).copied() + pub fn lookup(&self, module_name: &ModuleName) -> Option { + self.modules + .get_with_index(module_name) + .map(|(index, module_id)| LookedupModule { + id: *module_id, + params: self.params.get(index).copied().unwrap(), + }) } - pub fn has_id(&self, module_id: ModuleId) -> bool { - self.sources.contains_key(&module_id) + pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option { + self.modules + .get_index_by_value(module_id) + .map(|index| LookedupModule { + id: *module_id, + params: self.params.get(index).copied().unwrap(), + }) } pub fn available_names(&self) -> impl Iterator { From 5aebb7b570f22866db3d8a6619848c9fefeaf30e Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 9 May 2024 22:54:36 -0300 Subject: [PATCH 004/203] Unqualified lookups return params --- crates/compiler/can/src/annotation.rs | 2 +- crates/compiler/can/src/def.rs | 11 +++++-- crates/compiler/can/src/expr.rs | 24 +++++++------- crates/compiler/can/src/scope.rs | 47 +++++++++++++++++---------- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 5c6f458a879..c1cc17b2985 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -386,7 +386,7 @@ pub(crate) fn make_apply_symbol( // Look it up in scope! match scope.lookup_str(ident, region) { - Ok(symbol) => { + Ok(LookedupSymbol { symbol, params: _ }) => { references.insert_type_lookup(symbol, QualifiedReference::Unqualified); Ok(symbol) } diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 02f26511a39..7d9f3a341ff 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -515,6 +515,7 @@ fn canonicalize_claimed_ability_impl<'a>( let LookedupSymbol { symbol: member_symbol, + // todo(agus): params in abilities? params: _, } = match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) { Ok(symbol) => symbol, @@ -561,7 +562,8 @@ fn canonicalize_claimed_ability_impl<'a>( // reference. We want to check for a direct reference second so that if there is a // shadow, we won't accidentally grab the imported symbol. let opt_impl_symbol = (scope.lookup_ability_member_shadow(member_symbol)) - .or_else(|| scope.lookup_str(label_str, region).ok()); + // todo(agus): params in abilities? + .or_else(|| scope.lookup_str(label_str, region).map(|s| s.symbol).ok()); match opt_impl_symbol { // It's possible that even if we find a symbol it is still only the member @@ -609,6 +611,7 @@ fn canonicalize_claimed_ability_impl<'a>( let LookedupSymbol { symbol: member_symbol, + // todo(agus): params in abilities? params: _, } = match env.qualified_lookup_with_module_id( scope, @@ -627,7 +630,11 @@ fn canonicalize_claimed_ability_impl<'a>( } }; - let impl_symbol = match scope.lookup(&impl_ident.into(), impl_region) { + let LookedupSymbol { + symbol: impl_symbol, + // todo(agus): params in abilities? + params: _, + } = match scope.lookup(&impl_ident.into(), impl_region) { Ok(symbol) => symbol, Err(err) => { env.problem(Problem::RuntimeError(err)); diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 3ce8269914a..18c9cecf1b0 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1859,19 +1859,20 @@ fn canonicalize_var_lookup( // Since module_name was empty, this is an unqualified var. // Look it up in scope! match scope.lookup_str(ident, region) { - Ok(symbol) => { + Ok(lookup) => { output .references - .insert_value_lookup(symbol, QualifiedReference::Unqualified); + .insert_value_lookup(lookup.symbol, QualifiedReference::Unqualified); - if scope.abilities_store.is_ability_member_name(symbol) { + if scope.abilities_store.is_ability_member_name(lookup.symbol) { AbilityMember( - symbol, + // todo(agus): params for abilities? + lookup.symbol, Some(scope.abilities_store.fresh_specialization_id()), var_store.fresh(), ) } else { - Var(symbol, var_store.fresh()) + Var(lookup, var_store.fresh()) } } Err(problem) => { @@ -1884,23 +1885,20 @@ fn canonicalize_var_lookup( // Since module_name was nonempty, this is a qualified var. // Look it up in the env! match env.qualified_lookup(scope, module_name, ident, region) { - Ok(lookedup_symbol) => { + Ok(lookup) => { output .references - .insert_value_lookup(lookedup_symbol.symbol, QualifiedReference::Qualified); + .insert_value_lookup(lookup.symbol, QualifiedReference::Qualified); - if scope - .abilities_store - .is_ability_member_name(lookedup_symbol.symbol) - { + if scope.abilities_store.is_ability_member_name(lookup.symbol) { // todo(agus): params for abilities? AbilityMember( - lookedup_symbol.symbol, + lookup.symbol, Some(scope.abilities_store.fresh_specialization_id()), var_store.fresh(), ) } else { - Var(lookedup_symbol, var_store.fresh()) + Var(lookup, var_store.fresh()) } } Err(problem) => { diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index c157659c375..c45c8732bf3 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -1,7 +1,7 @@ use roc_collections::{VecMap, VecSet}; use roc_error_macros::internal_error; use roc_module::ident::{Ident, ModuleName}; -use roc_module::symbol::{IdentId, IdentIds, ModuleId, ScopeModules, Symbol}; +use roc_module::symbol::{IdentId, IdentIds, LookedupSymbol, ModuleId, ScopeModules, Symbol}; use roc_problem::can::RuntimeError; use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; @@ -33,6 +33,7 @@ pub struct Scope { pub modules: ScopeModules, /// Identifiers that are imported + // todo(agus): move to ScopeModules? imported_symbols: Vec<(Ident, Symbol, Region)>, /// Shadows of an ability member, for example a local specialization of `eq` for the ability @@ -76,7 +77,7 @@ impl Scope { } } - pub fn lookup(&self, ident: &Ident, region: Region) -> Result { + pub fn lookup(&self, ident: &Ident, region: Region) -> Result { self.lookup_str(ident.as_str(), region) } @@ -91,7 +92,7 @@ impl Scope { .push(("Set".into(), Symbol::SET_SET, Region::zero())); } - pub fn lookup_str(&self, ident: &str, region: Region) -> Result { + pub fn lookup_str(&self, ident: &str, region: Region) -> Result { use ContainsIdent::*; match self.scope_contains_ident(ident) { @@ -205,14 +206,19 @@ impl Scope { } } - fn has_imported_symbol(&self, ident: &str) -> Option<(Symbol, Region)> { - for (import, shadow, original_region) in self.imported_symbols.iter() { - if ident == import.as_str() { - return Some((*shadow, *original_region)); - } - } - - None + fn has_imported_symbol(&self, ident: &str) -> Option<(LookedupSymbol, Region)> { + self.imported_symbols + .iter() + .find_map(|(import, symbol, original_region)| { + if ident == import.as_str() { + match self.modules.lookup_by_id(&symbol.module_id()) { + Some(module) => Some((module.into_symbol(*symbol), *original_region)), + None => Some((LookedupSymbol::no_params(*symbol), *original_region)), + } + } else { + None + } + }) } /// Is an identifier in scope, either in the locals or imports @@ -229,7 +235,7 @@ impl Scope { ContainsIdent::InScope(original_symbol, original_region) => { // the ident is already in scope; up to the caller how to handle that // (usually it's shadowing, but it is valid to shadow ability members) - Err((original_symbol, original_region)) + Err((original_symbol.symbol, original_region)) } ContainsIdent::NotPresent => { // We know nothing about this ident yet; introduce it to the scope @@ -389,7 +395,9 @@ impl Scope { region: Region, ) -> Result<(), (Symbol, Region)> { match self.scope_contains_ident(ident.as_str()) { - ContainsIdent::InScope(symbol, region) => Err((symbol, region)), + ContainsIdent::InScope(LookedupSymbol { symbol, params: _ }, region) => { + Err((symbol, region)) + } ContainsIdent::NotPresent | ContainsIdent::NotInScope(_) => { self.imported_symbols.push((ident, symbol, region)); Ok(()) @@ -534,7 +542,7 @@ pub fn create_alias( #[derive(Debug)] enum ContainsIdent { - InScope(Symbol, Region), + InScope(LookedupSymbol, Region), NotInScope(IdentId), NotPresent, } @@ -561,7 +569,7 @@ impl ScopedIdentIds { fn has_in_scope(&self, ident: &Ident) -> Option<(Symbol, Region)> { match self.contains_ident(ident.as_str()) { - ContainsIdent::InScope(symbol, region) => Some((symbol, region)), + ContainsIdent::InScope(symbol, region) => Some((symbol.symbol, region)), ContainsIdent::NotInScope(_) | ContainsIdent::NotPresent => None, } } @@ -574,7 +582,10 @@ impl ScopedIdentIds { for ident_id in self.ident_ids.get_id_many(ident) { let index = ident_id.index(); if self.in_scope[index] { - return InScope(Symbol::new(self.home, ident_id), self.regions[index]); + return InScope( + LookedupSymbol::no_params(Symbol::new(self.home, ident_id)), + self.regions[index], + ); } else { result = NotInScope(ident_id) } @@ -701,7 +712,7 @@ mod test { let lookup = scope.lookup(&ident, Region::zero()).unwrap(); - assert_eq!(first, lookup); + assert_eq!(first, lookup.symbol); } #[test] @@ -857,6 +868,6 @@ mod test { let lookup = scope.lookup(&ident, Region::zero()).unwrap(); - assert_eq!(symbol, lookup); + assert_eq!(symbol, lookup.symbol); } } From 1526fc4aee65cc5818581d97d72fa3be80fa6d69 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 9 May 2024 23:58:05 -0300 Subject: [PATCH 005/203] can::Expr::ParamsVar for lookups with params --- crates/compiler/can/src/copy.rs | 9 +++++ crates/compiler/can/src/debug/pretty_print.rs | 4 ++- crates/compiler/can/src/expr.rs | 35 +++++++++++++++++-- crates/compiler/can/src/module.rs | 1 + crates/compiler/can/src/traverse.rs | 1 + crates/compiler/constrain/src/expr.rs | 19 +++++++++- crates/compiler/mono/src/ir.rs | 3 ++ 7 files changed, 67 insertions(+), 5 deletions(-) diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 5be29af2cd7..554c6047fec 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -288,6 +288,15 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr loc_elems: loc_elems.iter().map(|le| le.map(|e| go_help!(e))).collect(), }, Var(sym, var) => Var(*sym, sub!(*var)), + ParamsVar { + symbol, + params, + var, + } => ParamsVar { + symbol: *symbol, + params: *params, + var: sub!(*var), + }, &AbilityMember(sym, specialization, specialization_var) => { AbilityMember(sym, specialization, sub!(specialization_var)) } diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index 03467c9bde5..b8aac00f369 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -206,7 +206,9 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, .append("]") .group(), ), - Var(sym, _) | AbilityMember(sym, _, _) => pp_sym(c, f, *sym), + Var(sym, _) | ParamsVar { symbol: sym, .. } | AbilityMember(sym, _, _) => { + pp_sym(c, f, *sym) + } When { loc_cond, branches, .. } => maybe_paren!( diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 18c9cecf1b0..3804f5f6aaa 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -17,7 +17,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; -use roc_module::symbol::Symbol; +use roc_module::symbol::{LookedupSymbol, Symbol}; use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral}; use roc_parse::ident::Accessor; use roc_parse::pattern::PatternType::*; @@ -107,6 +107,12 @@ pub enum Expr { // Lookups Var(Symbol, Variable), + /// Like Var, but from a module with params + ParamsVar { + symbol: Symbol, + params: Symbol, + var: Variable, + }, AbilityMember( /// Actual member name Symbol, @@ -308,6 +314,11 @@ impl Expr { Self::SingleQuote(..) => Category::Character, Self::List { .. } => Category::List, &Self::Var(sym, _) => Category::Lookup(sym), + &Self::ParamsVar { + symbol, + params: _, + var: _, + } => Category::Lookup(symbol), &Self::AbilityMember(sym, _, _) => Category::Lookup(sym), Self::When { .. } => Category::When, Self::If { .. } => Category::If, @@ -1872,7 +1883,7 @@ fn canonicalize_var_lookup( var_store.fresh(), ) } else { - Var(lookup, var_store.fresh()) + lookup_to_expr(lookup, var_store.fresh()) } } Err(problem) => { @@ -1898,7 +1909,7 @@ fn canonicalize_var_lookup( var_store.fresh(), ) } else { - Var(lookup, var_store.fresh()) + lookup_to_expr(lookup, var_store.fresh()) } } Err(problem) => { @@ -1916,6 +1927,18 @@ fn canonicalize_var_lookup( (can_expr, output) } +fn lookup_to_expr(LookedupSymbol { symbol, params }: LookedupSymbol, var: Variable) -> Expr { + if let Some(params) = params { + Expr::ParamsVar { + symbol, + params, + var, + } + } else { + Expr::Var(symbol, var) + } +} + /// Currently uses the heuristic of "only inline if it's a builtin" pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { use Expr::*; @@ -1934,6 +1957,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { | other @ RecordAccessor { .. } | other @ RecordUpdate { .. } | other @ Var(..) + | other @ ParamsVar { .. } | other @ AbilityMember(..) | other @ RunLowLevel { .. } | other @ TypedHole { .. } @@ -3100,6 +3124,11 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec { while let Some(expr) = stack.pop() { match expr { Expr::Var(symbol, var) + | Expr::ParamsVar { + symbol, + params: _, + var, + } | Expr::RecordUpdate { symbol, record_var: var, diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index d866a043860..fa8234a6a09 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -1105,6 +1105,7 @@ fn fix_values_captured_in_closure_expr( | SingleQuote(..) | IngestedFile(..) | Var(..) + | ParamsVar { .. } | AbilityMember(..) | EmptyRecord | TypedHole { .. } diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index 3f044eed37f..98bd70982e5 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -266,6 +266,7 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { walk_list(visitor, *elem_var, loc_elems); } Expr::Var(..) => { /* terminal */ } + Expr::ParamsVar { .. } => { /* terminal */ } Expr::AbilityMember(..) => { /* terminal */ } Expr::If { cond_var, diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 5ec8153eb82..819f8b7e116 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -575,6 +575,22 @@ pub fn constrain_expr( constraints.and_constraint([store_expected, lookup_constr]) } + ParamsVar { + symbol, + params, + var, + } => { + // Save the expectation in the variable, then lookup the symbol's type in the environment + let expected_type = *constraints[expected].get_type_ref(); + let store_expected = constraints.store(expected_type, *var, file!(), line!()); + + let lookup_constr = constraints.lookup(*symbol, expected, region); + + // todo(agus): check params + // let params_constr = constraints.lookup(*params, expected, region); + + constraints.and_constraint([store_expected, lookup_constr]) + } &AbilityMember(symbol, specialization_id, specialization_var) => { // Save the expectation in the `specialization_var` so we know what to specialize, then // lookup the member in the environment. @@ -4115,7 +4131,8 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool { | ZeroArgumentTag { .. } | Tag { .. } | AbilityMember(..) - | Var(..) => return false, + | Var(..) + | ParamsVar { .. } => return false, } } } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index a813d434205..c64042eb054 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4422,6 +4422,9 @@ pub fn with_hole<'a>( specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol) } + ParamsVar { .. } => { + todo!("agus: handle params var") + } AbilityMember(member, specialization_id, specialization_var) => { let specialization_symbol = late_resolve_ability_specialization( env, From d14b1f805c20fce18dbe80ef71f8a9b10fdb9d41 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 23 May 2024 07:57:50 -0300 Subject: [PATCH 006/203] Fix LookedupSymbol reference in docs --- crates/docs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/docs/src/lib.rs b/crates/docs/src/lib.rs index 69c2168bc5b..2ff8341d562 100644 --- a/crates/docs/src/lib.rs +++ b/crates/docs/src/lib.rs @@ -933,7 +933,7 @@ fn doc_url<'a>( // current module's name, but it also could be a different // module - for example, if this is in scope from an // unqualified import. - module_name = symbol.module_string(interns); + module_name = symbol.symbol.module_string(interns); } Err(_) => { return Err((format!("[{ident}]"), LinkProblem::AutoLinkIdentNotInScope)); From c6e42ecf0ca7858a57241c6a9f3e833049fe3f4d Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 23 May 2024 08:05:38 -0300 Subject: [PATCH 007/203] Canonicalize module param patterns --- crates/compiler/can/src/module.rs | 36 +++++++++++++++-- crates/compiler/load_internal/src/file.rs | 2 + crates/compiler/parse/src/header.rs | 49 ++++++++++++++++++++++- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index fa8234a6a09..8c20679a469 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -8,7 +8,7 @@ use crate::env::Env; use crate::expr::{ ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives, }; -use crate::pattern::{BindingsFromPattern, Pattern}; +use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows}; use crate::procedure::References; use crate::scope::Scope; use bumpalo::Bump; @@ -18,7 +18,7 @@ use roc_module::ident::Ident; use roc_module::ident::Lowercase; use roc_module::symbol::{IdentIds, IdentIdsByModule, ModuleId, PackageModuleIds, Symbol}; use roc_parse::ast::{Defs, TypeAnnotation}; -use roc_parse::header::HeaderType; +use roc_parse::header::{HeaderType, ModuleParams}; use roc_parse::pattern::PatternType; use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Loc, Region}; @@ -149,6 +149,7 @@ pub struct RigidVariables { pub struct ModuleOutput { pub aliases: MutMap, pub rigid_variables: RigidVariables, + pub param_patterns: Vec, pub declarations: Declarations, pub exposed_imports: MutMap, pub exposed_symbols: VecSet, @@ -244,6 +245,7 @@ impl GeneratedInfo { generates_with, name: _, exposes: _, + opt_params: _, } => { debug_assert!(generates_with.is_empty()); GeneratedInfo::Builtin @@ -274,7 +276,7 @@ fn has_no_implementation(expr: &Expr) -> bool { pub fn canonicalize_module_defs<'a>( arena: &'a Bump, loc_defs: &'a mut Defs<'a>, - header_type: &roc_parse::header::HeaderType, + header_type: &'a roc_parse::header::HeaderType, home: ModuleId, module_path: &'a str, src: &'a str, @@ -290,6 +292,7 @@ pub fn canonicalize_module_defs<'a>( opt_shorthand: Option<&'a str>, ) -> ModuleOutput { let mut can_exposed_imports = MutMap::default(); + let mut scope = Scope::new( home, qualified_module_ids @@ -382,9 +385,33 @@ pub fn canonicalize_module_defs<'a>( } } + let mut output = Output::default(); + + let mut param_patterns = Vec::new(); + + if let Some(ModuleParams { params, .. }) = header_type.get_params() { + param_patterns.reserve_exact(params.len()); + + for param in params.iter() { + let pattern = canonicalize_pattern( + &mut env, + var_store, + &mut scope, + &mut output, + // todo(agus): custom type for param + PatternType::FunctionArg, + ¶m.value, + param.region, + PermitShadows(false), + ); + + param_patterns.push(pattern.value); + } + } + let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs( &mut env, - Output::default(), + output, var_store, &mut scope, loc_defs, @@ -812,6 +839,7 @@ pub fn canonicalize_module_defs<'a>( scope, aliases, rigid_variables, + param_patterns, declarations, referenced_values, exposed_imports: can_exposed_imports, diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 06ded0003d6..9af7b74cf30 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -3549,6 +3549,7 @@ fn load_builtin_module_help<'a>( name: header::ModuleName::new(name_stem), exposes: unspace(arena, header.exposes.items), generates_with: &[], + opt_params: header.params, }, module_comments: comments, header_imports: header.interface_imports, @@ -3833,6 +3834,7 @@ fn parse_header<'a>( header_type: HeaderType::Module { name: roc_parse::header::ModuleName::new(module_name), exposes: unspace(arena, header.exposes.items), + opt_params: header.params, }, module_comments: comments, header_imports: header.interface_imports, diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 6bbda2cea7e..6e9cc4b9908 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -55,6 +55,7 @@ pub enum HeaderType<'a> { name: ModuleName<'a>, exposes: &'a [Loc>], generates_with: &'a [Symbol], + opt_params: Option>, }, Package { /// usually something other than `pf` @@ -78,6 +79,7 @@ pub enum HeaderType<'a> { Module { name: ModuleName<'a>, exposes: &'a [Loc>], + opt_params: Option>, }, } @@ -102,12 +104,57 @@ impl<'a> HeaderType<'a> { } } + pub fn get_params(&self) -> &Option> { + match self { + Self::Module { + opt_params, + name: _, + exposes: _, + } + | Self::Builtin { + opt_params, + name: _, + exposes: _, + generates_with: _, + } => opt_params, + Self::App { + provides: _, + to_platform: _, + } + | Self::Package { + config_shorthand: _, + exposes: _, + exposes_ids: _, + } + | Self::Hosted { + name: _, + exposes: _, + generates: _, + generates_with: _, + } + | Self::Platform { + opt_app_module_id: _, + provides: _, + requires: _, + requires_types: _, + exposes: _, + exposes_ids: _, + config_shorthand: _, + } => &None, + } + } + pub fn to_maybe_builtin(self, module_id: ModuleId) -> Self { match self { - HeaderType::Module { name, exposes } if module_id.is_builtin() => HeaderType::Builtin { + HeaderType::Module { + name, + exposes, + opt_params, + } if module_id.is_builtin() => HeaderType::Builtin { name, exposes, generates_with: &[], + opt_params, }, _ => self, } From 7ac72159e9593ea8bc1398ef97632292bb04dd62 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 27 Jun 2024 16:14:56 -0300 Subject: [PATCH 008/203] Parse module params loc and rename to pattern --- crates/compiler/can/src/module.rs | 6 +++--- crates/compiler/fmt/src/module.rs | 10 ++++++++-- crates/compiler/fmt/src/spaces.rs | 2 +- crates/compiler/parse/src/header.rs | 2 +- crates/compiler/parse/src/module.rs | 2 +- ...with_multiline_params_and_exposes.header.result-ast | 2 +- .../pass/module_with_optional_param.header.result-ast | 2 +- .../pass/module_with_params.header.result-ast | 2 +- ...with_params_and_multiline_exposes.header.result-ast | 2 +- crates/language_server/src/analysis/tokens.rs | 4 ++-- 10 files changed, 20 insertions(+), 14 deletions(-) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 8c20679a469..0345412c986 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -389,10 +389,10 @@ pub fn canonicalize_module_defs<'a>( let mut param_patterns = Vec::new(); - if let Some(ModuleParams { params, .. }) = header_type.get_params() { - param_patterns.reserve_exact(params.len()); + if let Some(ModuleParams { pattern, .. }) = header_type.get_params() { + param_patterns.reserve_exact(pattern.value.len()); - for param in params.iter() { + for param in pattern.value.iter() { let pattern = canonicalize_pattern( &mut env, var_store, diff --git a/crates/compiler/fmt/src/module.rs b/crates/compiler/fmt/src/module.rs index fc0db50567d..e938c125c3b 100644 --- a/crates/compiler/fmt/src/module.rs +++ b/crates/compiler/fmt/src/module.rs @@ -182,11 +182,17 @@ pub fn fmt_module_header<'a>(buf: &mut Buf, header: &'a ModuleHeader<'a>) { let mut indent = fmt_spaces_with_outdent(buf, header.after_keyword, 0); if let Some(params) = &header.params { - if is_collection_multiline(¶ms.params) { + if is_collection_multiline(¶ms.pattern.value) { indent = INDENT; } - fmt_collection(buf, indent, Braces::Curly, params.params, Newlines::Yes); + fmt_collection( + buf, + indent, + Braces::Curly, + params.pattern.value, + Newlines::Yes, + ); indent = fmt_spaces_with_outdent(buf, params.before_arrow, indent); buf.push_str("->"); diff --git a/crates/compiler/fmt/src/spaces.rs b/crates/compiler/fmt/src/spaces.rs index 774d06fa5c3..e2a392b0f19 100644 --- a/crates/compiler/fmt/src/spaces.rs +++ b/crates/compiler/fmt/src/spaces.rs @@ -342,7 +342,7 @@ impl<'a> RemoveSpaces<'a> for Module<'a> { impl<'a> RemoveSpaces<'a> for ModuleParams<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { ModuleParams { - params: self.params.remove_spaces(arena), + pattern: self.pattern.remove_spaces(arena), before_arrow: &[], after_arrow: &[], } diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 6e9cc4b9908..017e24cd753 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -302,7 +302,7 @@ pub struct ModuleHeader<'a> { #[derive(Clone, Debug, PartialEq)] pub struct ModuleParams<'a> { - pub params: Collection<'a, Loc>>, + pub pattern: Loc>>>, pub before_arrow: &'a [CommentOrNewline<'a>], pub after_arrow: &'a [CommentOrNewline<'a>], } diff --git a/crates/compiler/parse/src/module.rs b/crates/compiler/parse/src/module.rs index 3a895cdd2a0..1a20ae13c22 100644 --- a/crates/compiler/parse/src/module.rs +++ b/crates/compiler/parse/src/module.rs @@ -123,7 +123,7 @@ fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> { fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> { record!(ModuleParams { - params: specialize_err(EParams::Pattern, record_pattern_fields()), + pattern: specialize_err(EParams::Pattern, loc(record_pattern_fields())), before_arrow: skip_second( space0_e(EParams::BeforeArrow), loc(two_bytes(b'-', b'>', EParams::Arrow)) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast index 6fd765312d3..eb15c7199e7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast @@ -5,7 +5,7 @@ Module { after_keyword: [], params: Some( ModuleParams { - params: [ + pattern: @7-45 [ @8-12 Identifier { ident: "echo", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast index 139cf2718db..18c02dffb83 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast @@ -5,7 +5,7 @@ Module { after_keyword: [], params: Some( ModuleParams { - params: [ + pattern: @7-20 [ @9-10 Identifier { ident: "x", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast index a39e3e01bf9..6f93ed27299 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast @@ -5,7 +5,7 @@ Module { after_keyword: [], params: Some( ModuleParams { - params: [ + pattern: @7-20 [ @8-12 Identifier { ident: "echo", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast index 5c31993249b..aa3dd3acf8b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast @@ -5,7 +5,7 @@ Module { after_keyword: [], params: Some( ModuleParams { - params: [ + pattern: @7-20 [ @8-12 Identifier { ident: "echo", }, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 869817080bf..ddfbae33a00 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -243,12 +243,12 @@ where impl IterTokens for ModuleParams<'_> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc> { let Self { - params, + pattern, before_arrow: _, after_arrow: _, } = self; - params.iter_tokens(arena) + pattern.value.iter_tokens(arena) } } From 3a1d3d4ddbd726e2b7b4798d426df49c005f6f68 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 23 May 2024 09:35:08 -0300 Subject: [PATCH 009/203] Reuse record destructure can for module params --- crates/compiler/can/src/module.rs | 34 ++-- crates/compiler/can/src/pattern.rs | 273 ++++++++++++++++------------- 2 files changed, 164 insertions(+), 143 deletions(-) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 0345412c986..d045c5fed41 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -8,7 +8,9 @@ use crate::env::Env; use crate::expr::{ ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives, }; -use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows}; +use crate::pattern::{ + canonicalize_record_destructure, BindingsFromPattern, Pattern, PermitShadows, +}; use crate::procedure::References; use crate::scope::Scope; use bumpalo::Bump; @@ -149,7 +151,7 @@ pub struct RigidVariables { pub struct ModuleOutput { pub aliases: MutMap, pub rigid_variables: RigidVariables, - pub param_patterns: Vec, + pub params_pattern: Option, pub declarations: Declarations, pub exposed_imports: MutMap, pub exposed_symbols: VecSet, @@ -387,27 +389,25 @@ pub fn canonicalize_module_defs<'a>( let mut output = Output::default(); - let mut param_patterns = Vec::new(); - - if let Some(ModuleParams { pattern, .. }) = header_type.get_params() { - param_patterns.reserve_exact(pattern.value.len()); - - for param in pattern.value.iter() { - let pattern = canonicalize_pattern( + let params_pattern = header_type.get_params().as_ref().map( + |ModuleParams { + pattern, + before_arrow: _, + after_arrow: _, + }| { + canonicalize_record_destructure( &mut env, var_store, &mut scope, &mut output, // todo(agus): custom type for param PatternType::FunctionArg, - ¶m.value, - param.region, + &pattern.value, + pattern.region, PermitShadows(false), - ); - - param_patterns.push(pattern.value); - } - } + ) + }, + ); let (defs, output, symbols_introduced, imports_introduced) = canonicalize_defs( &mut env, @@ -839,7 +839,7 @@ pub fn canonicalize_module_defs<'a>( scope, aliases, rigid_variables, - param_patterns, + params_pattern, declarations, referenced_values, exposed_imports: can_exposed_imports, diff --git a/crates/compiler/can/src/pattern.rs b/crates/compiler/can/src/pattern.rs index dc3a7d7dfe3..64e6b446aba 100644 --- a/crates/compiler/can/src/pattern.rs +++ b/crates/compiler/can/src/pattern.rs @@ -623,132 +623,16 @@ pub fn canonicalize_pattern<'a>( } } - RecordDestructure(patterns) => { - let ext_var = var_store.fresh(); - let whole_var = var_store.fresh(); - let mut destructs = Vec::with_capacity(patterns.len()); - let mut opt_erroneous = None; - - for loc_pattern in patterns.iter() { - match loc_pattern.value { - Identifier { ident: label } => { - match scope.introduce(label.into(), region) { - Ok(symbol) => { - output.references.insert_bound(symbol); - - destructs.push(Loc { - region: loc_pattern.region, - value: RecordDestruct { - var: var_store.fresh(), - label: Lowercase::from(label), - symbol, - typ: DestructType::Required, - }, - }); - } - Err((shadowed_symbol, shadow, new_symbol)) => { - env.problem(Problem::RuntimeError(RuntimeError::Shadowing { - original_region: shadowed_symbol.region, - shadow: shadow.clone(), - kind: ShadowKind::Variable, - })); - - // No matter what the other patterns - // are, we're definitely shadowed and will - // get a runtime exception as soon as we - // encounter the first bad pattern. - opt_erroneous = Some(Pattern::Shadowed( - shadowed_symbol.region, - shadow, - new_symbol, - )); - } - }; - } - - RequiredField(label, loc_guard) => { - // a guard does not introduce the label into scope! - let symbol = - scope.scopeless_symbol(&Ident::from(label), loc_pattern.region); - let can_guard = canonicalize_pattern( - env, - var_store, - scope, - output, - pattern_type, - &loc_guard.value, - loc_guard.region, - permit_shadows, - ); - - destructs.push(Loc { - region: loc_pattern.region, - value: RecordDestruct { - var: var_store.fresh(), - label: Lowercase::from(label), - symbol, - typ: DestructType::Guard(var_store.fresh(), can_guard), - }, - }); - } - OptionalField(label, loc_default) => { - // an optional DOES introduce the label into scope! - match scope.introduce(label.into(), region) { - Ok(symbol) => { - let (can_default, expr_output) = canonicalize_expr( - env, - var_store, - scope, - loc_default.region, - &loc_default.value, - ); - - // an optional field binds the symbol! - output.references.insert_bound(symbol); - - output.union(expr_output); - - destructs.push(Loc { - region: loc_pattern.region, - value: RecordDestruct { - var: var_store.fresh(), - label: Lowercase::from(label), - symbol, - typ: DestructType::Optional(var_store.fresh(), can_default), - }, - }); - } - Err((shadowed_symbol, shadow, new_symbol)) => { - env.problem(Problem::RuntimeError(RuntimeError::Shadowing { - original_region: shadowed_symbol.region, - shadow: shadow.clone(), - kind: ShadowKind::Variable, - })); - - // No matter what the other patterns - // are, we're definitely shadowed and will - // get a runtime exception as soon as we - // encounter the first bad pattern. - opt_erroneous = Some(Pattern::Shadowed( - shadowed_symbol.region, - shadow, - new_symbol, - )); - } - }; - } - _ => unreachable!("Any other pattern should have given a parse error"), - } - } - - // If we encountered an erroneous pattern (e.g. one with shadowing), - // use the resulting RuntimeError. Otherwise, return a successful record destructure. - opt_erroneous.unwrap_or(Pattern::RecordDestructure { - whole_var, - ext_var, - destructs, - }) - } + RecordDestructure(patterns) => canonicalize_record_destructure( + env, + var_store, + scope, + output, + pattern_type, + patterns, + region, + permit_shadows, + ), RequiredField(_name, _loc_pattern) => { unreachable!("should have been handled in RecordDestructure"); @@ -894,6 +778,143 @@ pub fn canonicalize_pattern<'a>( } } +pub fn canonicalize_record_destructure<'a>( + env: &mut Env<'a>, + var_store: &mut VarStore, + scope: &mut Scope, + output: &mut Output, + pattern_type: PatternType, + patterns: &ast::Collection>>, + region: Region, + permit_shadows: PermitShadows, +) -> Pattern { + use ast::Pattern::*; + + let ext_var = var_store.fresh(); + let whole_var = var_store.fresh(); + let mut destructs = Vec::with_capacity(patterns.len()); + let mut opt_erroneous = None; + + for loc_pattern in patterns.iter() { + match loc_pattern.value { + Identifier { ident: label } => { + match scope.introduce(label.into(), region) { + Ok(symbol) => { + output.references.insert_bound(symbol); + + destructs.push(Loc { + region: loc_pattern.region, + value: RecordDestruct { + var: var_store.fresh(), + label: Lowercase::from(label), + symbol, + typ: DestructType::Required, + }, + }); + } + Err((shadowed_symbol, shadow, new_symbol)) => { + env.problem(Problem::RuntimeError(RuntimeError::Shadowing { + original_region: shadowed_symbol.region, + shadow: shadow.clone(), + kind: ShadowKind::Variable, + })); + + // No matter what the other patterns + // are, we're definitely shadowed and will + // get a runtime exception as soon as we + // encounter the first bad pattern. + opt_erroneous = Some(Pattern::Shadowed( + shadowed_symbol.region, + shadow, + new_symbol, + )); + } + }; + } + + RequiredField(label, loc_guard) => { + // a guard does not introduce the label into scope! + let symbol = scope.scopeless_symbol(&Ident::from(label), loc_pattern.region); + let can_guard = canonicalize_pattern( + env, + var_store, + scope, + output, + pattern_type, + &loc_guard.value, + loc_guard.region, + permit_shadows, + ); + + destructs.push(Loc { + region: loc_pattern.region, + value: RecordDestruct { + var: var_store.fresh(), + label: Lowercase::from(label), + symbol, + typ: DestructType::Guard(var_store.fresh(), can_guard), + }, + }); + } + OptionalField(label, loc_default) => { + // an optional DOES introduce the label into scope! + match scope.introduce(label.into(), region) { + Ok(symbol) => { + let (can_default, expr_output) = canonicalize_expr( + env, + var_store, + scope, + loc_default.region, + &loc_default.value, + ); + + // an optional field binds the symbol! + output.references.insert_bound(symbol); + + output.union(expr_output); + + destructs.push(Loc { + region: loc_pattern.region, + value: RecordDestruct { + var: var_store.fresh(), + label: Lowercase::from(label), + symbol, + typ: DestructType::Optional(var_store.fresh(), can_default), + }, + }); + } + Err((shadowed_symbol, shadow, new_symbol)) => { + env.problem(Problem::RuntimeError(RuntimeError::Shadowing { + original_region: shadowed_symbol.region, + shadow: shadow.clone(), + kind: ShadowKind::Variable, + })); + + // No matter what the other patterns + // are, we're definitely shadowed and will + // get a runtime exception as soon as we + // encounter the first bad pattern. + opt_erroneous = Some(Pattern::Shadowed( + shadowed_symbol.region, + shadow, + new_symbol, + )); + } + }; + } + _ => unreachable!("Any other pattern should have given a parse error"), + } + } + + // If we encountered an erroneous pattern (e.g. one with shadowing), + // use the resulting RuntimeError. Otherwise, return a successful record destructure. + opt_erroneous.unwrap_or(Pattern::RecordDestructure { + whole_var, + ext_var, + destructs, + }) +} + /// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't /// assign to Int patterns), report it to Env and return an UnsupportedPattern runtime error pattern. fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) -> Pattern { From 717463079a6d0c934155da0a3badf5db54beeb22 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 27 Jun 2024 16:12:30 -0300 Subject: [PATCH 010/203] Add module params to module cache --- crates/compiler/can/src/module.rs | 15 ++++++++++++--- crates/compiler/can/src/pattern.rs | 1 + crates/compiler/load_internal/src/file.rs | 9 +++++++++ crates/compiler/load_internal/src/module_cache.rs | 6 +++++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index d045c5fed41..68832ccce52 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -6,7 +6,7 @@ use crate::def::{canonicalize_defs, report_unused_imports, Def}; use crate::effect_module::HostedGeneratedFunctions; use crate::env::Env; use crate::expr::{ - ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives, + AnnotatedMark, ClosureData, DbgLookup, Declarations, ExpectLookup, Expr, Output, PendingDerives, }; use crate::pattern::{ canonicalize_record_destructure, BindingsFromPattern, Pattern, PermitShadows, @@ -138,6 +138,7 @@ pub struct Module { pub abilities_store: PendingAbilitiesStore, pub loc_expects: VecMap>, pub loc_dbgs: VecMap, + pub params_pattern: Option<(Variable, AnnotatedMark, Loc)>, } #[derive(Debug, Default)] @@ -151,7 +152,7 @@ pub struct RigidVariables { pub struct ModuleOutput { pub aliases: MutMap, pub rigid_variables: RigidVariables, - pub params_pattern: Option, + pub params_pattern: Option<(Variable, AnnotatedMark, Loc)>, pub declarations: Declarations, pub exposed_imports: MutMap, pub exposed_symbols: VecSet, @@ -395,7 +396,7 @@ pub fn canonicalize_module_defs<'a>( before_arrow: _, after_arrow: _, }| { - canonicalize_record_destructure( + let can_pattern = canonicalize_record_destructure( &mut env, var_store, &mut scope, @@ -405,6 +406,14 @@ pub fn canonicalize_module_defs<'a>( &pattern.value, pattern.region, PermitShadows(false), + ); + + let loc_pattern = Loc::at(pattern.region, can_pattern); + + ( + var_store.fresh(), + AnnotatedMark::new(var_store), + loc_pattern, ) }, ); diff --git a/crates/compiler/can/src/pattern.rs b/crates/compiler/can/src/pattern.rs index 64e6b446aba..129236bb462 100644 --- a/crates/compiler/can/src/pattern.rs +++ b/crates/compiler/can/src/pattern.rs @@ -778,6 +778,7 @@ pub fn canonicalize_pattern<'a>( } } +#[allow(clippy::too_many_arguments)] pub fn canonicalize_record_destructure<'a>( env: &mut Env<'a>, var_store: &mut VarStore, diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 9af7b74cf30..700e9532ee5 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -2395,6 +2395,13 @@ fn update<'a>( .pending_abilities .insert(module_id, constrained_module.module.abilities_store.clone()); + if let Some(params_pattern) = constrained_module.module.params_pattern.clone() { + state + .module_cache + .param_patterns + .insert(module_id, params_pattern); + } + state .module_cache .constrained @@ -5112,6 +5119,7 @@ fn canonicalize_and_constrain<'a>( abilities_store: module_output.scope.abilities_store, loc_expects: module_output.loc_expects, loc_dbgs: module_output.loc_dbgs, + params_pattern: module_output.params_pattern, }; let constrained_module = ConstrainedModule { @@ -5455,6 +5463,7 @@ fn make_specializations<'a>( ) -> Msg<'a> { let make_specializations_start = Instant::now(); let mut update_mode_ids = UpdateModeIds::new(); + // do the thing let mut mono_env = roc_mono::ir::Env { arena, diff --git a/crates/compiler/load_internal/src/module_cache.rs b/crates/compiler/load_internal/src/module_cache.rs index 0c202d93d15..ed62692a260 100644 --- a/crates/compiler/load_internal/src/module_cache.rs +++ b/crates/compiler/load_internal/src/module_cache.rs @@ -4,12 +4,13 @@ use crate::module::{ ModuleHeader, ParsedModule, TypeCheckedModule, }; use roc_can::abilities::PendingAbilitiesStore; +use roc_can::expr::AnnotatedMark; use roc_collections::{MutMap, MutSet, VecMap}; use roc_module::ident::ModuleName; use roc_module::symbol::{ModuleId, PQModuleName, Symbol}; use roc_mono::ir::ExternalSpecializations; use roc_problem::Severity; -use roc_region::all::Region; +use roc_region::all::{Loc, Region}; use roc_solve_problem::TypeError; use roc_types::subs::Variable; use roc_types::types::Alias; @@ -26,6 +27,8 @@ pub(crate) struct ModuleCache<'a> { pub(crate) aliases: MutMap>, pub(crate) pending_abilities: MutMap, pub(crate) constrained: MutMap, + pub(crate) param_patterns: + MutMap)>, pub(crate) typechecked: MutMap>, pub(crate) checked: MutMap, pub(crate) found_specializations: MutMap>, @@ -100,6 +103,7 @@ impl Default for ModuleCache<'_> { aliases: Default::default(), pending_abilities: Default::default(), constrained: Default::default(), + param_patterns: Default::default(), typechecked: Default::default(), checked: Default::default(), found_specializations: Default::default(), From dd0e28240a3f1681978f1be74599e258e16cef5a Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 30 May 2024 20:11:51 -0300 Subject: [PATCH 011/203] Add module param identifiers to solve's scope --- crates/compiler/load/tests/helpers/mod.rs | 1 + crates/compiler/load_internal/src/file.rs | 2 ++ crates/compiler/solve/src/module.rs | 3 +++ crates/compiler/solve/src/solve.rs | 9 +++++-- crates/compiler/solve/src/solve/scope.rs | 32 ++++++++++++++++++++++- crates/compiler/test_derive/src/util.rs | 1 + 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index e8110e4d637..aa14df23262 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -51,6 +51,7 @@ pub fn infer_expr( exposed_by_module: &Default::default(), derived_module, function_kind: FunctionKind::LambdaSet, + params_pattern: None, #[cfg(debug_assertions)] checkmate: None, }; diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 700e9532ee5..b8c80c37923 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4613,6 +4613,7 @@ fn run_solve_solve( aliases, rigid_variables, abilities_store: pending_abilities, + params_pattern, .. } = module; @@ -4660,6 +4661,7 @@ fn run_solve_solve( derived_module, #[cfg(debug_assertions)] checkmate, + params_pattern: params_pattern.map(|(_, _, pattern)| pattern.value), }; let solve_output = roc_solve::module::run_solve( diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 77b4bf2aa67..dea688c527d 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -80,6 +80,9 @@ pub struct SolveConfig<'a> { #[cfg(debug_assertions)] /// The checkmate collector for this module. pub checkmate: Option, + + /// Module params pattern + pub params_pattern: Option, } pub struct SolveOutput { diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 95dafa4851d..92525f7f61f 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -129,15 +129,18 @@ fn run_help( exposed_by_module, derived_module, function_kind, + params_pattern, .. } = config; let mut pools = Pools::default(); + // todo(agus): Do we need state here? let state = State { - scope: Scope::default(), + scope: Scope::new(None), mark: Mark::NONE.next(), }; + let rank = Rank::toplevel(); let arena = Bump::new(); @@ -186,6 +189,7 @@ fn run_help( abilities_store, &mut obligation_cache, &mut awaiting_specializations, + params_pattern, ); RunSolveOutput { @@ -244,9 +248,10 @@ fn solve( abilities_store: &mut AbilitiesStore, obligation_cache: &mut ObligationCache, awaiting_specializations: &mut AwaitingSpecializations, + params_pattern: Option, ) -> State { let initial = Work::Constraint { - scope: &Scope::default(), + scope: &Scope::new(params_pattern), rank, constraint, }; diff --git a/crates/compiler/solve/src/solve/scope.rs b/crates/compiler/solve/src/solve/scope.rs index 5acd041c968..9214a9d6b29 100644 --- a/crates/compiler/solve/src/solve/scope.rs +++ b/crates/compiler/solve/src/solve/scope.rs @@ -2,13 +2,43 @@ use roc_module::symbol::Symbol; use roc_types::subs::Variable; /// The scope of the solver, as symbols are introduced. -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug)] pub struct Scope { symbols: Vec, variables: Vec, } impl Scope { + pub fn new(params_pattern: Option) -> Self { + match params_pattern { + Some(params_pattern) => match params_pattern { + roc_can::pattern::Pattern::RecordDestructure { + whole_var: _, + ext_var: _, + destructs, + } => { + let mut symbols = Vec::with_capacity(destructs.len()); + let mut variables = Vec::with_capacity(destructs.len()); + + for destruct in destructs { + symbols.push(destruct.value.symbol); + variables.push(destruct.value.var); + } + + Self { symbols, variables } + } + _ => unreachable!( + "other pattern types should have parsed: {:?}", + params_pattern + ), + }, + None => Self { + symbols: Vec::default(), + variables: Vec::default(), + }, + } + } + pub fn vars_by_symbol(&self) -> impl Iterator + '_ { let it1 = self.symbols.iter().copied(); let it2 = self.variables.iter().copied(); diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 0fbb02e9be4..8c800450603 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -438,6 +438,7 @@ fn check_derived_typechecks_and_golden( pending_derives: Default::default(), exposed_by_module: &exposed_for_module.exposed_by_module, derived_module: Default::default(), + params_pattern: None, #[cfg(debug_assertions)] checkmate: None, From dcb2767b6e812c9871e276c767d8e048401ffa71 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 30 May 2024 20:18:59 -0300 Subject: [PATCH 012/203] Do not create unnecessary scope in solve run_help --- crates/compiler/solve/src/solve.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 92525f7f61f..8b6bb0283a8 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -135,12 +135,6 @@ fn run_help( let mut pools = Pools::default(); - // todo(agus): Do we need state here? - let state = State { - scope: Scope::new(None), - mark: Mark::NONE.next(), - }; - let rank = Rank::toplevel(); let arena = Bump::new(); @@ -181,7 +175,6 @@ fn run_help( let state = solve( &mut env, types, - state, rank, problems, aliases, @@ -240,7 +233,6 @@ enum Work<'a> { fn solve( env: &mut InferenceEnv, mut can_types: Types, - mut state: State, rank: Rank, problems: &mut Vec, aliases: &mut Aliases, @@ -250,14 +242,21 @@ fn solve( awaiting_specializations: &mut AwaitingSpecializations, params_pattern: Option, ) -> State { + let scope = Scope::new(params_pattern); + let initial = Work::Constraint { - scope: &Scope::new(params_pattern), + scope: &scope.clone(), rank, constraint, }; let mut stack = vec![initial]; + let mut state = State { + scope, + mark: Mark::NONE.next(), + }; + while let Some(work_item) = stack.pop() { let (scope, rank, constraint) = match work_item { Work::Constraint { From 674adf1fad7d5e7c58ab698be49e0ffc05df4c90 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 30 May 2024 21:55:28 -0300 Subject: [PATCH 013/203] Wrap import params expr so we can constrain later --- crates/compiler/can/src/copy.rs | 3 ++ crates/compiler/can/src/debug/pretty_print.rs | 1 + crates/compiler/can/src/def.rs | 36 ++++++++++++++----- crates/compiler/can/src/expr.rs | 14 +++++++- crates/compiler/can/src/module.rs | 8 +++++ crates/compiler/can/src/traverse.rs | 1 + crates/compiler/constrain/src/expr.rs | 10 +++--- crates/compiler/mono/src/ir.rs | 9 +++++ 8 files changed, 68 insertions(+), 14 deletions(-) diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 554c6047fec..052f99318c7 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -297,6 +297,9 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr params: *params, var: sub!(*var), }, + ImportParams(loc_expr, module_id) => { + ImportParams(Box::new(loc_expr.map(|e| go_help!(e))), *module_id) + } &AbilityMember(sym, specialization, specialization_var) => { AbilityMember(sym, specialization, sub!(specialization_var)) } diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index b8aac00f369..b8d630fa348 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -209,6 +209,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, Var(sym, _) | ParamsVar { symbol: sym, .. } | AbilityMember(sym, _, _) => { pp_sym(c, f, *sym) } + ImportParams(loc_expr, _) => expr(c, p, f, &loc_expr.value), When { loc_cond, branches, .. } => maybe_paren!( diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 7d9f3a341ff..1d713453773 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -167,10 +167,11 @@ enum PendingValueDef<'a> { &'a Loc>, ), /// Module params from an import - ImportParams( - Loc, - ast::Collection<'a, Loc>>>, - ), + ImportParams { + loc_pattern: Loc, + module_id: ModuleId, + params: ast::Collection<'a, Loc>>>, + }, /// Ingested file IngestedFile( Loc, @@ -185,7 +186,11 @@ impl PendingValueDef<'_> { PendingValueDef::AnnotationOnly(_, loc_pattern, _) => loc_pattern, PendingValueDef::Body(loc_pattern, _) => loc_pattern, PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern, - PendingValueDef::ImportParams(loc_pattern, _) => loc_pattern, + PendingValueDef::ImportParams { + loc_pattern, + module_id: _, + params: _, + } => loc_pattern, PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern, } } @@ -1144,7 +1149,11 @@ fn canonicalize_value_defs<'a>( }); if let Some((loc_pattern, params)) = params { - pending_value_defs.push(PendingValueDef::ImportParams(loc_pattern, params)); + pending_value_defs.push(PendingValueDef::ImportParams { + loc_pattern, + module_id, + params, + }); } } PendingValue::InvalidIngestedFile => { /* skip */ } @@ -2387,13 +2396,22 @@ fn canonicalize_pending_value_def<'a>( None, ) } - ImportParams(loc_pattern, params) => { - let (expr, can_output) = + ImportParams { + loc_pattern, + module_id, + params, + } => { + let (record, can_output) = canonicalize_record(env, var_store, scope, loc_pattern.region, params); output.union(can_output); - let loc_expr = Loc::at(loc_pattern.region, expr); + let loc_record = Loc::at(loc_pattern.region, record); + + let loc_expr = Loc::at( + loc_pattern.region, + Expr::ImportParams(Box::new(loc_record), module_id), + ); let def = single_can_def( loc_pattern, diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 3804f5f6aaa..1b2c23ffcc3 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -17,7 +17,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; -use roc_module::symbol::{LookedupSymbol, Symbol}; +use roc_module::symbol::{LookedupSymbol, ModuleId, Symbol}; use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral}; use roc_parse::ident::Accessor; use roc_parse::pattern::PatternType::*; @@ -183,6 +183,9 @@ pub enum Expr { elems: Vec<(Variable, Box>)>, }, + /// Module params expression in import + ImportParams(Box>, ModuleId), + /// The "crash" keyword Crash { msg: Box>, @@ -335,6 +338,7 @@ impl Expr { Self::RecordAccessor(data) => Category::Accessor(data.field.clone()), Self::TupleAccess { index, .. } => Category::TupleAccess(*index), Self::RecordUpdate { .. } => Category::Record, + Self::ImportParams(loc_expr, _) => loc_expr.value.category(), Self::Tag { name, arguments, .. } => Category::TagApply { @@ -2221,6 +2225,11 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { ); } + ImportParams(loc_expr, module_id) => { + let loc_expr = Loc::at(loc_expr.region, inline_calls(var_store, loc_expr.value)); + ImportParams(Box::new(loc_expr), module_id) + } + RecordAccess { record_var, ext_var, @@ -3233,6 +3242,9 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec { Expr::Tuple { elems, .. } => { stack.extend(elems.iter().map(|(_, elem)| &elem.value)); } + Expr::ImportParams(loc_expr, _) => { + stack.push(&loc_expr.value); + } Expr::Expect { loc_continuation, .. } diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 68832ccce52..e8ee98c65df 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -1254,6 +1254,14 @@ fn fix_values_captured_in_closure_expr( } } + ImportParams(loc_expr, _) => { + fix_values_captured_in_closure_expr( + &mut loc_expr.value, + no_capture_symbols, + closure_captures, + ); + } + Tuple { elems, .. } => { for (_var, expr) in elems.iter_mut() { fix_values_captured_in_closure_expr( diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index 98bd70982e5..0c9c887cace 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -318,6 +318,7 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { .iter() .for_each(|(var, elem)| visitor.visit_expr(&elem.value, elem.region, *var)), Expr::EmptyRecord => { /* terminal */ } + Expr::ImportParams(expr, _) => visitor.visit_expr(&expr.value, expr.region, var), Expr::RecordAccess { field_var, loc_expr, diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 819f8b7e116..302ad694d38 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -577,7 +577,7 @@ pub fn constrain_expr( } ParamsVar { symbol, - params, + params: _, var, } => { // Save the expectation in the variable, then lookup the symbol's type in the environment @@ -586,11 +586,12 @@ pub fn constrain_expr( let lookup_constr = constraints.lookup(*symbol, expected, region); - // todo(agus): check params - // let params_constr = constraints.lookup(*params, expected, region); - constraints.and_constraint([store_expected, lookup_constr]) } + ImportParams(params, module_id) => { + // todo(agus): constrain + Constraint::True + } &AbilityMember(symbol, specialization_id, specialization_var) => { // Save the expectation in the `specialization_var` so we know what to specialize, then // lookup the member in the environment. @@ -4105,6 +4106,7 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool { return true; } OpaqueRef { argument, .. } => expr = &argument.1.value, + ImportParams(loc_expr, _) => expr = &loc_expr.value, Str(_) | IngestedFile(..) | List { .. } diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index c64042eb054..251ba27a533 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4425,6 +4425,15 @@ pub fn with_hole<'a>( ParamsVar { .. } => { todo!("agus: handle params var") } + ImportParams(loc_expr, _) => with_hole( + env, + loc_expr.value, + variable, + procs, + layout_cache, + assigned, + hole, + ), AbilityMember(member, specialization_id, specialization_var) => { let specialization_symbol = late_resolve_ability_specialization( env, From 8f69e75a9596a4341fbe3f55b6769d65208e8000 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 4 Jun 2024 17:20:02 -0300 Subject: [PATCH 014/203] Contrain module params pattern --- crates/compiler/constrain/src/module.rs | 56 ++++++++++++++++++++++- crates/compiler/load_internal/src/file.rs | 1 + 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index 545aa9d5a9b..17de140306c 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -1,11 +1,14 @@ use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env}; +use crate::pattern::{constrain_pattern, PatternState}; use roc_can::abilities::{PendingAbilitiesStore, PendingMemberType}; use roc_can::constraint::{Constraint, Constraints, Generalizable}; -use roc_can::expected::Expected; -use roc_can::expr::Declarations; +use roc_can::expected::{Expected, PExpected}; +use roc_can::expr::{AnnotatedMark, Declarations}; use roc_can::pattern::Pattern; +use roc_collections::MutMap; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::{Loc, Region}; +use roc_types::subs::Variable; use roc_types::types::{AnnotationSource, Category, Type, Types}; pub fn constrain_module( @@ -14,9 +17,18 @@ pub fn constrain_module( symbols_from_requires: Vec<(Loc, Loc)>, abilities_store: &PendingAbilitiesStore, declarations: &Declarations, + params_pattern: &Option<(Variable, AnnotatedMark, Loc)>, home: ModuleId, ) -> Constraint { let constraint = crate::expr::constrain_decls(types, constraints, home, declarations); + + let constraint = match params_pattern { + Some(params_pattern) => { + constrain_params(types, constraints, home, constraint, params_pattern) + } + None => constraint, + }; + let constraint = constrain_symbols_from_requires( types, constraints, @@ -33,6 +45,46 @@ pub fn constrain_module( constraint } +fn constrain_params( + types: &mut Types, + constraints: &mut Constraints, + home: ModuleId, + constraint: Constraint, + (pattern_var, _, loc_pattern): &(Variable, AnnotatedMark, Loc), +) -> Constraint { + let mut env = Env { + home, + rigids: MutMap::default(), + resolutions_to_make: vec![], + }; + + let index = constraints.push_variable(*pattern_var); + let expected_params = constraints.push_pat_expected_type(PExpected::NoExpectation(index)); + + let mut state = PatternState::default(); + + constrain_pattern( + types, + constraints, + &mut env, + &loc_pattern.value, + loc_pattern.region, + expected_params, + &mut state, + ); + + let pattern_constraints = constraints.and_constraint(state.constraints); + + constraints.let_constraint( + [], + state.vars, + state.headers, + pattern_constraints, + constraint, + Generalizable(true), + ) +} + fn constrain_symbols_from_requires( types: &mut Types, constraints: &mut Constraints, diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index b8c80c37923..e4daf714732 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -5065,6 +5065,7 @@ fn canonicalize_and_constrain<'a>( module_output.symbols_from_requires, &module_output.scope.abilities_store, &module_output.declarations, + &module_output.params_pattern, module_id, ) }; From 50b1a60411958b85a02eb054fdf1411191a6dead Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 27 Jun 2024 16:30:23 -0300 Subject: [PATCH 015/203] Reuse Var constrain for ParamsVar --- crates/compiler/constrain/src/expr.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 302ad694d38..fe415924078 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -566,23 +566,15 @@ pub fn constrain_expr( constraints.exists([*ret_var], and) } - Var(symbol, variable) => { - // Save the expectation in the variable, then lookup the symbol's type in the environment - let expected_type = *constraints[expected].get_type_ref(); - let store_expected = constraints.store(expected_type, *variable, file!(), line!()); - - let lookup_constr = constraints.lookup(*symbol, expected, region); - - constraints.and_constraint([store_expected, lookup_constr]) - } - ParamsVar { + Var(symbol, variable) + | ParamsVar { symbol, params: _, - var, + var: variable, } => { // Save the expectation in the variable, then lookup the symbol's type in the environment let expected_type = *constraints[expected].get_type_ref(); - let store_expected = constraints.store(expected_type, *var, file!(), line!()); + let store_expected = constraints.store(expected_type, *variable, file!(), line!()); let lookup_constr = constraints.lookup(*symbol, expected, region); From 8604bb23b20a81bf6f61ad748ab1e8e3b4ed0026 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 27 Jun 2024 17:51:02 -0300 Subject: [PATCH 016/203] Include param's record references --- crates/compiler/can/src/def.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 1d713453773..277bb41ea37 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2404,6 +2404,7 @@ fn canonicalize_pending_value_def<'a>( let (record, can_output) = canonicalize_record(env, var_store, scope, loc_pattern.region, params); + let references = DefReferences::Value(can_output.references.clone()); output.union(can_output); let loc_record = Loc::at(loc_pattern.region, record); @@ -2423,7 +2424,7 @@ fn canonicalize_pending_value_def<'a>( DefOutput { output, - references: DefReferences::Value(References::new()), + references, def, } } From c541dd5747aaa6c96c5a133d334ef4866093143f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 27 Jun 2024 17:56:38 -0300 Subject: [PATCH 017/203] Do not report import params symbol as unused --- crates/compiler/can/src/def.rs | 42 ++++++++++++------- .../compiler/load_internal/tests/test_load.rs | 31 ++++++++++++++ 2 files changed, 57 insertions(+), 16 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 277bb41ea37..094272f647d 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -19,6 +19,7 @@ use crate::expr::Expr::{self, *}; use crate::expr::StructAccessorData; use crate::expr::{canonicalize_expr, Output, Recursive}; use crate::pattern::{canonicalize_def_header_pattern, BindingsFromPattern, Pattern}; +use crate::procedure::QualifiedReference; use crate::procedure::References; use crate::scope::create_alias; use crate::scope::{PendingAbilitiesInScope, Scope}; @@ -168,6 +169,7 @@ enum PendingValueDef<'a> { ), /// Module params from an import ImportParams { + symbol: Symbol, loc_pattern: Loc, module_id: ModuleId, params: ast::Collection<'a, Loc>>>, @@ -188,6 +190,7 @@ impl PendingValueDef<'_> { PendingValueDef::TypedBody(_, loc_pattern, _, _) => loc_pattern, PendingValueDef::ImportParams { loc_pattern, + symbol: _, module_id: _, params: _, } => loc_pattern, @@ -1148,8 +1151,9 @@ fn canonicalize_value_defs<'a>( exposed_symbols, }); - if let Some((loc_pattern, params)) = params { + if let Some((symbol, loc_pattern, params)) = params { pending_value_defs.push(PendingValueDef::ImportParams { + symbol, loc_pattern, module_id, params, @@ -1602,7 +1606,7 @@ impl DefOrdering { fn insert_symbol_references(&mut self, def_id: u32, def_references: &DefReferences) { match def_references { DefReferences::Value(references) => { - let it = references.value_lookups().chain(references.calls()); + let it = references.value_lookups(); for referenced in it { if let Some(ref_id) = self.get_id(*referenced) { @@ -2397,6 +2401,7 @@ fn canonicalize_pending_value_def<'a>( ) } ImportParams { + symbol, loc_pattern, module_id, params, @@ -2404,6 +2409,12 @@ fn canonicalize_pending_value_def<'a>( let (record, can_output) = canonicalize_record(env, var_store, scope, loc_pattern.region, params); + // Insert a reference to the record so that we don't report it as unused + // If the whole module is unused, we'll report that separately + output + .references + .insert_value_lookup(symbol, QualifiedReference::Unqualified); + let references = DefReferences::Value(can_output.references.clone()); output.union(can_output); @@ -2974,6 +2985,7 @@ enum PendingValue<'a> { region: Region, exposed_symbols: Vec<(Symbol, Region)>, params: Option<( + Symbol, Loc, ast::Collection<'a, Loc>>>, )>, @@ -3138,25 +3150,30 @@ fn to_pending_value_def<'a>( None => module_name.clone(), }; - let params = if let Some(params) = module_import.params { + let mut params_sym = None; + + let params = module_import.params.and_then(|params| { let name_str = name_with_alias.as_str(); // todo(agus): params specific loc match scope.introduce_str(format!("#{name_str}").as_str(), region) { - Ok(sym) => Some((sym, params)), + Ok(sym) => { + params_sym = Some(sym); + + let loc_pattern = Loc::at(region, Pattern::Identifier(sym)); + Some((sym, loc_pattern, params.params)) + }, Err(_) => { - // Ignore conflict, it will be handled right after + // Ignore conflict, it will be handled as a duplicate import None } } - } else { - None - }; + }); if let Err(existing_import) = scope .modules - .insert(name_with_alias.clone(), module_id, params.map(|(sym, _)| sym), region) + .insert(name_with_alias.clone(), module_id, params_sym, region) { env.problems.push(Problem::ImportNameConflict { name: name_with_alias, @@ -3219,15 +3236,8 @@ fn to_pending_value_def<'a>( })) } } - } - let params = - params.map(|(sym, params)| { - // todo(agus): params specific loc - (Loc::at(region, Pattern::Identifier(sym)), params.params) - }); - PendingValue::ModuleImport { module_id, region, diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index bf921398d6c..ee58081c96e 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -1563,6 +1563,37 @@ fn cannot_use_original_name_if_imported_with_alias() { multiple_modules("cannot_use_original_name_if_imported_with_alias", modules).unwrap_err(); } +#[test] +fn import_module_params_no_warn() { + let modules = vec![ + ( + "Api.roc", + indoc!( + r#" + module { key } -> [url] + + url = "example.com/$(key)" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api { key: "abcdef" } + + example = Api.url + "# + ), + ), + ]; + + let result = multiple_modules("module_params_typecheck", modules); + assert!(result.is_ok()); +} + #[test] fn issue_2863_module_type_does_not_exist() { let modules = vec![ From 5ec4b042bbfd375c7abe2079820942abbf39bc2f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 27 Jun 2024 18:27:33 -0300 Subject: [PATCH 018/203] Constrain and solve import params No reporting yet --- crates/compiler/can/src/constraint.rs | 16 ++++- crates/compiler/can/src/copy.rs | 8 ++- crates/compiler/can/src/debug/pretty_print.rs | 2 +- crates/compiler/can/src/def.rs | 2 +- crates/compiler/can/src/expr.rs | 10 +-- crates/compiler/can/src/module.rs | 2 +- crates/compiler/can/src/traverse.rs | 2 +- crates/compiler/constrain/src/expr.rs | 23 +++++-- crates/compiler/constrain/src/module.rs | 6 +- crates/compiler/load/tests/helpers/mod.rs | 1 + crates/compiler/load_internal/src/file.rs | 59 +++++++++++++++- .../compiler/load_internal/tests/test_load.rs | 67 ++++++++++++++++++- crates/compiler/mono/src/ir.rs | 2 +- crates/compiler/solve/src/module.rs | 10 ++- crates/compiler/solve/src/solve.rs | 49 +++++++++++++- crates/compiler/test_derive/src/util.rs | 1 + crates/compiler/types/src/types.rs | 3 +- crates/reporting/src/error/type.rs | 1 + 18 files changed, 238 insertions(+), 26 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index 0f594d31a80..e4fa841d88b 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -602,7 +602,8 @@ impl Constraints { | Constraint::Exhaustive { .. } | Constraint::Resolve(..) | Constraint::IngestedFile(..) - | Constraint::CheckCycle(..) => false, + | Constraint::CheckCycle(..) + | Constraint::ImportParams(..) => false, } } @@ -685,6 +686,15 @@ impl Constraints { ) -> Constraint { Constraint::IngestedFile(type_index, file_path, bytes) } + + pub fn import_params( + &mut self, + type_index: TypeOrVar, + module_id: ModuleId, + region: Region, + ) -> Constraint { + Constraint::ImportParams(type_index, module_id, region) + } } roc_error_macros::assert_sizeof_default!(Constraint, 3 * 8); @@ -787,6 +797,7 @@ pub enum Constraint { CheckCycle(Index, IllegalCycleMark), IngestedFile(TypeOrVar, Box, Arc>), + ImportParams(TypeOrVar, ModuleId, Region), } #[derive(Debug, Clone, Copy, Default)] @@ -865,6 +876,9 @@ impl std::fmt::Debug for Constraint { Self::IngestedFile(arg0, arg1, arg2) => { write!(f, "IngestedFile({arg0:?}, {arg1:?}, {arg2:?})") } + Self::ImportParams(arg0, arg1, arg2) => { + write!(f, "ImportParams({arg0:?}, {arg1:?}, {arg2:?})") + } } } } diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 052f99318c7..41f27b53b9d 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -297,9 +297,11 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr params: *params, var: sub!(*var), }, - ImportParams(loc_expr, module_id) => { - ImportParams(Box::new(loc_expr.map(|e| go_help!(e))), *module_id) - } + ImportParams(loc_expr, var, module_id) => ImportParams( + Box::new(loc_expr.map(|e| go_help!(e))), + sub!(*var), + *module_id, + ), &AbilityMember(sym, specialization, specialization_var) => { AbilityMember(sym, specialization, sub!(specialization_var)) } diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index b8d630fa348..bb898737a4b 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -209,7 +209,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, Var(sym, _) | ParamsVar { symbol: sym, .. } | AbilityMember(sym, _, _) => { pp_sym(c, f, *sym) } - ImportParams(loc_expr, _) => expr(c, p, f, &loc_expr.value), + ImportParams(loc_expr, _, _) => expr(c, p, f, &loc_expr.value), When { loc_cond, branches, .. } => maybe_paren!( diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 094272f647d..bd7367a4c32 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2422,7 +2422,7 @@ fn canonicalize_pending_value_def<'a>( let loc_expr = Loc::at( loc_pattern.region, - Expr::ImportParams(Box::new(loc_record), module_id), + Expr::ImportParams(Box::new(loc_record), var_store.fresh(), module_id), ); let def = single_can_def( diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 1b2c23ffcc3..33b2acfb5e2 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -184,7 +184,7 @@ pub enum Expr { }, /// Module params expression in import - ImportParams(Box>, ModuleId), + ImportParams(Box>, Variable, ModuleId), /// The "crash" keyword Crash { @@ -338,7 +338,7 @@ impl Expr { Self::RecordAccessor(data) => Category::Accessor(data.field.clone()), Self::TupleAccess { index, .. } => Category::TupleAccess(*index), Self::RecordUpdate { .. } => Category::Record, - Self::ImportParams(loc_expr, _) => loc_expr.value.category(), + Self::ImportParams(loc_expr, _, _) => loc_expr.value.category(), Self::Tag { name, arguments, .. } => Category::TagApply { @@ -2225,9 +2225,9 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { ); } - ImportParams(loc_expr, module_id) => { + ImportParams(loc_expr, var, module_id) => { let loc_expr = Loc::at(loc_expr.region, inline_calls(var_store, loc_expr.value)); - ImportParams(Box::new(loc_expr), module_id) + ImportParams(Box::new(loc_expr), var, module_id) } RecordAccess { @@ -3242,7 +3242,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec { Expr::Tuple { elems, .. } => { stack.extend(elems.iter().map(|(_, elem)| &elem.value)); } - Expr::ImportParams(loc_expr, _) => { + Expr::ImportParams(loc_expr, _, _) => { stack.push(&loc_expr.value); } Expr::Expect { diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index e8ee98c65df..bd468d224ad 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -1254,7 +1254,7 @@ fn fix_values_captured_in_closure_expr( } } - ImportParams(loc_expr, _) => { + ImportParams(loc_expr, _, _) => { fix_values_captured_in_closure_expr( &mut loc_expr.value, no_capture_symbols, diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index 0c9c887cace..3809ab57c23 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -318,7 +318,7 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { .iter() .for_each(|(var, elem)| visitor.visit_expr(&elem.value, elem.region, *var)), Expr::EmptyRecord => { /* terminal */ } - Expr::ImportParams(expr, _) => visitor.visit_expr(&expr.value, expr.region, var), + Expr::ImportParams(expr, _, _) => visitor.visit_expr(&expr.value, expr.region, var), Expr::RecordAccess { field_var, loc_expr, diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index fe415924078..3e27dc9941f 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -580,9 +580,24 @@ pub fn constrain_expr( constraints.and_constraint([store_expected, lookup_constr]) } - ImportParams(params, module_id) => { - // todo(agus): constrain - Constraint::True + ImportParams(params, var, module_id) => { + let index = constraints.push_variable(*var); + let expected_params = constraints.push_expected_type(Expected::ForReason( + Reason::ImportParams(*module_id), + index, + params.region, + )); + let expr_con = constrain_expr( + types, + constraints, + env, + params.region, + ¶ms.value, + expected_params, + ); + let params_con = constraints.import_params(index, *module_id, params.region); + let expr_and_params = constraints.and_constraint([expr_con, params_con]); + constraints.exists([*var], expr_and_params) } &AbilityMember(symbol, specialization_id, specialization_var) => { // Save the expectation in the `specialization_var` so we know what to specialize, then @@ -4098,7 +4113,7 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool { return true; } OpaqueRef { argument, .. } => expr = &argument.1.value, - ImportParams(loc_expr, _) => expr = &loc_expr.value, + ImportParams(loc_expr, _, _) => expr = &loc_expr.value, Str(_) | IngestedFile(..) | List { .. } diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index 17de140306c..dedb172fbe4 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -75,14 +75,16 @@ fn constrain_params( let pattern_constraints = constraints.and_constraint(state.constraints); - constraints.let_constraint( + let cons = constraints.let_constraint( [], state.vars, state.headers, pattern_constraints, constraint, Generalizable(true), - ) + ); + + constraints.exists([*pattern_var], cons) } fn constrain_symbols_from_requires( diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index aa14df23262..63679c98208 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -52,6 +52,7 @@ pub fn infer_expr( derived_module, function_kind: FunctionKind::LambdaSet, params_pattern: None, + module_params_vars: Default::default(), #[cfg(debug_assertions)] checkmate: None, }; diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index e4daf714732..f000bf61c23 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -340,6 +340,21 @@ fn start_phase<'a>( None }; + let mut module_params_vars = Vec::with_capacity(available_modules.len()); + + for (module_id, _) in available_modules.iter() { + if let Some((_, _, pat)) = state.module_cache.param_patterns.get(module_id) { + match pat.value { + roc_can::pattern::Pattern::RecordDestructure { whole_var, .. } => { + module_params_vars.push((*module_id, whole_var)); + } + _ => { + internal_error!("Module params can only be records"); + } + } + } + } + BuildTask::solve_module( module, ident_ids, @@ -351,6 +366,7 @@ fn start_phase<'a>( pending_derives, var_store, available_modules, + module_params_vars, &state.exposed_types, dep_idents, declarations, @@ -905,6 +921,7 @@ enum BuildTask<'a> { dep_idents: IdentIdsByModule, cached_subs: CachedTypeState, derived_module: SharedDerivedModule, + module_params_vars: Vec<(ModuleId, Variable)>, #[cfg(debug_assertions)] checkmate: Option, @@ -4301,6 +4318,7 @@ impl<'a> BuildTask<'a> { pending_derives: PendingDerives, var_store: VarStore, imported_modules: MutMap, + module_params_vars: Vec<(ModuleId, Variable)>, exposed_types: &ExposedByModule, dep_idents: IdentIdsByModule, declarations: Declarations, @@ -4329,6 +4347,7 @@ impl<'a> BuildTask<'a> { module_timing, cached_subs, derived_module, + module_params_vars, #[cfg(debug_assertions)] checkmate, @@ -4597,6 +4616,7 @@ struct SolveResult { #[allow(clippy::complexity)] fn run_solve_solve( exposed_for_module: ExposedForModule, + original_param_vars: Vec<(ModuleId, Variable)>, mut types: Types, mut constraints: Constraints, constraint: ConstraintSoa, @@ -4623,7 +4643,7 @@ fn run_solve_solve( let mut subs = Subs::new_from_varstore(var_store); - let (import_variables, abilities_store) = add_imports( + let (mut import_variables, abilities_store) = add_imports( module.module_id, &mut constraints, &mut subs, @@ -4634,6 +4654,31 @@ fn run_solve_solve( &mut imported_flex_vars, ); + let mut imported_param_vars = HashMap::with_capacity(original_param_vars.len()); + + for (module_id, params_var) in original_param_vars.iter() { + let ExposedModuleTypes { + exposed_types_storage_subs: exposed_types, + resolved_implementations: _, + } = exposed_for_module + .exposed_by_module + .get(&module_id) + .unwrap(); + + let copied_import = exposed_types + .storage_subs + .export_variable_to(&mut subs, *params_var); + + let copied_import_var = extend_imports_data_with_copied_import( + copied_import, + &mut import_variables, + &mut imported_rigid_vars, + &mut imported_flex_vars, + ); + + imported_param_vars.insert(*module_id, copied_import_var); + } + let actual_constraint = constraints.let_import_constraint( imported_rigid_vars, imported_flex_vars, @@ -4662,6 +4707,7 @@ fn run_solve_solve( #[cfg(debug_assertions)] checkmate, params_pattern: params_pattern.map(|(_, _, pattern)| pattern.value), + module_params_vars: imported_param_vars, }; let solve_output = roc_solve::module::run_solve( @@ -4726,6 +4772,7 @@ fn run_solve<'a>( ident_ids: IdentIds, mut module_timing: ModuleTiming, exposed_for_module: ExposedForModule, + module_params_vars: Vec<(ModuleId, Variable)>, types: Types, constraints: Constraints, constraint: ConstraintSoa, @@ -4746,6 +4793,11 @@ fn run_solve<'a>( // TODO remove when we write builtins in roc let aliases = module.aliases.clone(); + let opt_params_var = match module.params_pattern { + Some((params_var, _, _)) => Some(params_var), + None => None, + }; + let mut module = module; let loc_expects = std::mem::take(&mut module.loc_expects); let loc_dbgs = std::mem::take(&mut module.loc_dbgs); @@ -4756,6 +4808,7 @@ fn run_solve<'a>( match cached_types.lock().remove(&module_id) { None => run_solve_solve( exposed_for_module, + module_params_vars, types, constraints, constraint, @@ -4787,6 +4840,7 @@ fn run_solve<'a>( } else { run_solve_solve( exposed_for_module, + module_params_vars, types, constraints, constraint, @@ -4817,6 +4871,7 @@ fn run_solve<'a>( module_id, &mut solved_subs, &exposed_vars_by_symbol, + opt_params_var, &solved_implementations, &abilities_store, ); @@ -6202,6 +6257,7 @@ fn run_task<'a>( dep_idents, cached_subs, derived_module, + module_params_vars, #[cfg(debug_assertions)] checkmate, @@ -6210,6 +6266,7 @@ fn run_task<'a>( ident_ids, module_timing, exposed_for_module, + module_params_vars, types, constraints, constraint, diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index ee58081c96e..ddd2b88b539 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -1564,7 +1564,7 @@ fn cannot_use_original_name_if_imported_with_alias() { } #[test] -fn import_module_params_no_warn() { +fn module_params_checks() { let modules = vec![ ( "Api.roc", @@ -1590,10 +1590,73 @@ fn import_module_params_no_warn() { ), ]; - let result = multiple_modules("module_params_typecheck", modules); + let result = multiple_modules("module_params_checks", modules); assert!(result.is_ok()); } +#[test] +fn module_params_optional() { + let modules = vec![ + ( + "Api.roc", + indoc!( + r#" + module { key, exp ? "default" } -> [url] + + url = "example.com/$(key)?exp=$(exp)" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api { key: "abcdef" } + + example = Api.url + "# + ), + ), + ]; + + let result = multiple_modules("module_params_optional", modules); + assert!(result.is_ok()) +} + +#[test] +#[should_panic] +fn module_params_typecheck_fail() { + let modules = vec![ + ( + "Api.roc", + indoc!( + r#" + module { key } -> [url] + + url = "example.com/$(key)" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api { key: 123 } + + example = Api.url + "# + ), + ), + ]; + + multiple_modules("module_params_typecheck", modules).unwrap_err(); + // todo(agus): test reporting +} + #[test] fn issue_2863_module_type_does_not_exist() { let modules = vec![ diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 251ba27a533..3f036a5c006 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4425,7 +4425,7 @@ pub fn with_hole<'a>( ParamsVar { .. } => { todo!("agus: handle params var") } - ImportParams(loc_expr, _) => with_hole( + ImportParams(loc_expr, _, _) => with_hole( env, loc_expr.value, variable, diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index dea688c527d..1a140595e9b 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::solve::RunSolveOutput; use crate::FunctionKind; use crate::{aliases::Aliases, solve}; @@ -81,8 +83,9 @@ pub struct SolveConfig<'a> { /// The checkmate collector for this module. pub checkmate: Option, - /// Module params pattern + /// Module params pub params_pattern: Option, + pub module_params_vars: HashMap, } pub struct SolveOutput { @@ -147,6 +150,7 @@ pub fn exposed_types_storage_subs( home: ModuleId, solved_subs: &mut Solved, exposed_vars_by_symbol: &[(Symbol, Variable)], + params_var: Option, solved_implementations: &ResolvedImplementations, abilities_store: &AbilitiesStore, ) -> ExposedTypesStorageSubs { @@ -159,6 +163,10 @@ pub fn exposed_types_storage_subs( stored_vars_by_symbol.insert(*symbol, new_var); } + if let Some(params_var) = params_var { + storage_subs.import_variable_from(subs, params_var); + } + let mut stored_specialization_lambda_set_vars = VecMap::with_capacity(solved_implementations.len()); diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 8b6bb0283a8..3ba8bf03c36 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::ability::{ resolve_ability_specialization, type_implementing_specialization, AbilityImplError, CheckedDerives, ObligationCache, PendingDerivesTable, Resolved, @@ -20,7 +22,7 @@ use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; use roc_error_macros::internal_error; -use roc_module::symbol::Symbol; +use roc_module::symbol::{ModuleId, Symbol}; use roc_problem::can::CycleEntry; use roc_region::all::Loc; use roc_solve_problem::TypeError; @@ -130,6 +132,7 @@ fn run_help( derived_module, function_kind, params_pattern, + module_params_vars, .. } = config; @@ -183,6 +186,7 @@ fn run_help( &mut obligation_cache, &mut awaiting_specializations, params_pattern, + module_params_vars, ); RunSolveOutput { @@ -241,6 +245,7 @@ fn solve( obligation_cache: &mut ObligationCache, awaiting_specializations: &mut AwaitingSpecializations, params_pattern: Option, + module_params_vars: HashMap, ) -> State { let scope = Scope::new(params_pattern); @@ -1396,6 +1401,48 @@ fn solve( } } } + ImportParams(type_index, module_id, _region) => { + let actual = either_type_index_to_var( + env, + rank, + problems, + abilities_store, + obligation_cache, + &mut can_types, + aliases, + *type_index, + ); + + let expected = module_params_vars + .get(module_id) + // todo(agus): Module has no params? handle + .unwrap(); + + match unify( + &mut env.uenv(), + actual, + *expected, + UnificationMode::EQ, + Polarity::OF_VALUE, + ) { + Success { + vars, + must_implement_ability: _, + lambda_sets_to_specialize: _, + extra_metadata: _, + } => { + env.introduce(rank, &vars); + + state + } + + Failure(vars, _actual_type, _expected_type, _) => { + env.introduce(rank, &vars); + + todo!("agus: reporting") + } + } + } }; } diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 8c800450603..746c152e4c6 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -439,6 +439,7 @@ fn check_derived_typechecks_and_golden( exposed_by_module: &exposed_for_module.exposed_by_module, derived_module: Default::default(), params_pattern: None, + module_params_vars: Default::default(), #[cfg(debug_assertions)] checkmate: None, diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index d04d1f96905..f6757247c9e 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -11,7 +11,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; -use roc_module::symbol::{Interns, Symbol}; +use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_region::all::{Loc, Region}; use std::fmt; use std::fmt::Write; @@ -3424,6 +3424,7 @@ pub enum Reason { def_region: Region, }, CrashArg, + ImportParams(ModuleId), } #[derive(PartialEq, Eq, Debug, Clone)] diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 1b3d9e4062e..da4bb0ed098 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1557,6 +1557,7 @@ fn to_expr_report<'b>( Reason::RecordDefaultField(_) => { unimplemented!("record default field is not implemented yet") } + Reason::ImportParams(_) => todo!("agus: report bad params"), }, } } From f0fe0a3ea6a6895700890f0e1233ed17e62d5797 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 1 Jul 2024 15:37:05 -0300 Subject: [PATCH 019/203] Module params are not extensible --- crates/compiler/constrain/src/module.rs | 16 +++++++++ .../compiler/load_internal/tests/test_load.rs | 36 +++++++++++++++++-- crates/compiler/solve/src/solve.rs | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/crates/compiler/constrain/src/module.rs b/crates/compiler/constrain/src/module.rs index dedb172fbe4..f1742216b85 100644 --- a/crates/compiler/constrain/src/module.rs +++ b/crates/compiler/constrain/src/module.rs @@ -6,6 +6,7 @@ use roc_can::expected::{Expected, PExpected}; use roc_can::expr::{AnnotatedMark, Declarations}; use roc_can::pattern::Pattern; use roc_collections::MutMap; +use roc_error_macros::internal_error; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; @@ -63,6 +64,21 @@ fn constrain_params( let mut state = PatternState::default(); + let closed_con = match loc_pattern.value { + Pattern::RecordDestructure { + whole_var: _, + ext_var, + destructs: _, + } => { + // Disallow record extension for module params + let empty_rec = constraints.push_type(types, Types::EMPTY_RECORD); + constraints.store(empty_rec, ext_var, file!(), line!()) + } + _ => internal_error!("Only record destructures are allowed in module params. This should've been caught earlier."), + }; + + state.constraints.push(closed_con); + constrain_pattern( types, constraints, diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index ddd2b88b539..0161fdc7e80 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -1626,7 +1626,7 @@ fn module_params_optional() { } #[test] -#[should_panic] +#[should_panic(expected = "report import params mismatch")] fn module_params_typecheck_fail() { let modules = vec![ ( @@ -1653,7 +1653,39 @@ fn module_params_typecheck_fail() { ), ]; - multiple_modules("module_params_typecheck", modules).unwrap_err(); + let _ = multiple_modules("module_params_typecheck_fail", modules); + // todo(agus): test reporting +} + +#[test] +#[should_panic(expected = "report import params mismatch")] +fn module_params_typecheck_extra_fields() { + let modules = vec![ + ( + "Api.roc", + indoc!( + r#" + module { key } -> [url] + + url = "example.com/$(key)" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api { key: "123", doesNotExist: Bool.true } + + example = Api.url + "# + ), + ), + ]; + + let _ = multiple_modules("module_params_typecheck_extra_fields", modules); // todo(agus): test reporting } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 3ba8bf03c36..358a5d5f621 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1439,7 +1439,7 @@ fn solve( Failure(vars, _actual_type, _expected_type, _) => { env.introduce(rank, &vars); - todo!("agus: reporting") + todo!("report import params mismatch") } } } From 90c7745989ed2ec2302db4be1e77f5bab3217627 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 1 Jul 2024 18:03:42 -0300 Subject: [PATCH 020/203] Capture import params location for better errors --- crates/compiler/can/src/def.rs | 9 +++++---- crates/compiler/can/src/desugar.rs | 4 +++- crates/compiler/fmt/src/def.rs | 4 ++-- crates/compiler/parse/src/ast.rs | 4 ++-- crates/compiler/parse/src/expr.rs | 11 ++++++++--- .../pass/import_with_params.moduledefs.result-ast | 8 ++++---- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index bd7367a4c32..5c9f61e9e2b 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -3155,13 +3155,14 @@ fn to_pending_value_def<'a>( let params = module_import.params.and_then(|params| { let name_str = name_with_alias.as_str(); - // todo(agus): params specific loc - match scope.introduce_str(format!("#{name_str}").as_str(), region) { + let params_region = params.params.region; + + match scope.introduce_str(format!("#{name_str}").as_str(), params_region) { Ok(sym) => { params_sym = Some(sym); - let loc_pattern = Loc::at(region, Pattern::Identifier(sym)); - Some((sym, loc_pattern, params.params)) + let loc_pattern = Loc::at(params_region, Pattern::Identifier(sym)); + Some((sym, loc_pattern, params.params.value)) }, Err(_) => { // Ignore conflict, it will be handled as a duplicate import diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index f72c91cabd5..957b3ee9e7e 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -140,7 +140,9 @@ fn desugar_value_def<'a>( let desugared_params = params.map(|ModuleImportParams { before, params }| ModuleImportParams { before, - params: desugar_field_collection(arena, params, src, line_info, module_path), + params: params.map(|params| { + desugar_field_collection(arena, *params, src, line_info, module_path) + }), }); ModuleImport(roc_parse::ast::ModuleImport { diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 95c3ecdfb61..4b8acc12cb1 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -252,14 +252,14 @@ impl<'a> Formattable for ModuleImportParams<'a> { fn is_multiline(&self) -> bool { let ModuleImportParams { before, params } = self; - !before.is_empty() || is_collection_multiline(params) + !before.is_empty() || is_collection_multiline(¶ms.value) } fn format_with_options(&self, buf: &mut Buf, _parens: Parens, newlines: Newlines, indent: u16) { let ModuleImportParams { before, params } = self; fmt_default_spaces(buf, before, indent); - fmt_collection(buf, indent, Braces::Curly, *params, newlines); + fmt_collection(buf, indent, Braces::Curly, params.value, newlines); } } diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index a3371688b0a..4682d51e24f 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -1031,7 +1031,7 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> { params, }) => { if let Some(ModuleImportParams { before: _, params }) = params { - for loc_assigned_field in params.items { + for loc_assigned_field in params.value.items { if let Some(expr) = loc_assigned_field.value.value() { self.push_pending_from_expr(&expr.value); } @@ -1079,7 +1079,7 @@ pub struct ModuleImport<'a> { #[derive(Debug, Clone, Copy, PartialEq)] pub struct ModuleImportParams<'a> { pub before: &'a [CommentOrNewline<'a>], - pub params: Collection<'a, Loc>>>, + pub params: Loc>>>>, } #[derive(Debug, Clone, Copy, PartialEq)] diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index f02dfe79beb..db48e009966 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -993,9 +993,11 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams< then( and( backtrackable(space0_e(EImportParams::Indent)), - specialize_err(EImportParams::Record, record_help()), + specialize_err(EImportParams::Record, loc(record_help())), ), - |arena, state, _, (before, record): (_, RecordHelp<'a>)| { + |arena, state, _, (before, loc_record): (_, Loc>)| { + let record = loc_record.value; + if let Some(update) = record.update { return Err(( MadeProgress, @@ -1013,7 +1015,10 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams< } })?; - let import_params = ModuleImportParams { before, params }; + let import_params = ModuleImportParams { + before, + params: Loc::at(loc_record.region, params), + }; Ok((MadeProgress, import_params, state)) }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast index d3dd6f81cbb..e6c74363f49 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast @@ -44,7 +44,7 @@ Defs { params: Some( ModuleImportParams { before: [], - params: [ + params: @15-29 [ @17-21 LabelOnly( @17-21 "echo", ), @@ -70,7 +70,7 @@ Defs { params: Some( ModuleImportParams { before: [], - params: [ + params: @42-60 [ @44-48 LabelOnly( @44-48 "echo", ), @@ -101,7 +101,7 @@ Defs { params: Some( ModuleImportParams { before: [], - params: [ + params: @73-91 [ @75-79 LabelOnly( @75-79 "echo", ), @@ -143,7 +143,7 @@ Defs { params: Some( ModuleImportParams { before: [], - params: [ + params: @109-123 [ @111-115 LabelOnly( @111-115 "echo", ), From 702092859ee60b70a308571a8f51b4c448dfaab7 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 1 Jul 2024 18:06:41 -0300 Subject: [PATCH 021/203] Add pattern type for module params --- crates/compiler/can/src/module.rs | 3 +-- crates/compiler/parse/src/pattern.rs | 1 + crates/reporting/src/error/canonicalize.rs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index bd468d224ad..bd602f8516d 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -401,8 +401,7 @@ pub fn canonicalize_module_defs<'a>( var_store, &mut scope, &mut output, - // todo(agus): custom type for param - PatternType::FunctionArg, + PatternType::ModuleParams, &pattern.value, pattern.region, PermitShadows(false), diff --git a/crates/compiler/parse/src/pattern.rs b/crates/compiler/parse/src/pattern.rs index a9dbf051d7a..1f3f9bfe5f1 100644 --- a/crates/compiler/parse/src/pattern.rs +++ b/crates/compiler/parse/src/pattern.rs @@ -25,6 +25,7 @@ pub enum PatternType { DefExpr, FunctionArg, WhenBranch, + ModuleParams, } pub fn closure_param<'a>() -> impl Parser<'a, Loc>, EPattern<'a>> { diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 41957cdf3f4..4d7bf9774b8 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -390,6 +390,7 @@ pub fn can_problem<'b>( TopLevelDef => "a top-level definition:", DefExpr => "a value definition:", FunctionArg => "function arguments:", + ModuleParams => "module params:", WhenBranch => unreachable!("all patterns are allowed in a When"), }; From 42c58d8efe464557cc2ed8ebeb22bfba9a898a9f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 1 Jul 2024 18:20:20 -0300 Subject: [PATCH 022/203] Use Vec instead of VecMap for scope module sources --- crates/compiler/can/src/scope.rs | 1 - crates/compiler/module/src/symbol.rs | 17 +++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index c45c8732bf3..4ba1bf6002c 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -33,7 +33,6 @@ pub struct Scope { pub modules: ScopeModules, /// Identifiers that are imported - // todo(agus): move to ScopeModules? imported_symbols: Vec<(Ident, Symbol, Region)>, /// Shadows of an ability member, for example a local specialization of `eq` for the ability diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index a3a31b69c24..b626c9b273f 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -641,7 +641,7 @@ impl LookedupSymbol { #[derive(Debug, Clone)] pub struct ScopeModules { modules: VecMap, - sources: VecMap, // todo(agus): why not Vec? + sources: Vec, params: Vec>, } @@ -698,17 +698,16 @@ impl ScopeModules { params_symbol: Option, region: Region, ) -> Result<(), ScopeModuleSource> { - if let Some(existing_module_id) = self.modules.get(&module_name) { + if let Some((index, existing_module_id)) = self.modules.get_with_index(&module_name) { if *existing_module_id == module_id { return Ok(()); } - return Err(*self.sources.get(existing_module_id).unwrap()); + return Err(*self.sources.get(index).unwrap()); } self.modules.insert(module_name, module_id); - self.sources - .insert(module_id, ScopeModuleSource::Import(region)); + self.sources.push(ScopeModuleSource::Import(region)); self.params.push(params_symbol); Ok(()) } @@ -1078,14 +1077,12 @@ macro_rules! define_builtins { let capacity = $total + 1; let mut modules = VecMap::with_capacity(capacity); - let mut sources = VecMap::with_capacity(capacity); + let mut sources = Vec::with_capacity(capacity); let mut params = Vec::with_capacity(capacity); - // todo(agus): Use insert - if !home_id.is_builtin() { modules.insert(home_name, home_id); - sources.insert(home_id, ScopeModuleSource::Current); + sources.push(ScopeModuleSource::Current); params.push(None); } @@ -1093,7 +1090,7 @@ macro_rules! define_builtins { let name: ModuleName = name_str.into(); modules.insert(name, id); - sources.insert(id, ScopeModuleSource::Builtin); + sources.push(ScopeModuleSource::Builtin); params.push(None); }; From db76ab4015dedf3a38e4c02692196a0e55a15586 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 1 Jul 2024 18:40:48 -0300 Subject: [PATCH 023/203] Replace params in abilities TODOs with unimplemented! --- crates/compiler/can/src/def.rs | 64 +++++++++++++++++---------------- crates/compiler/can/src/expr.rs | 7 ++-- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 5c9f61e9e2b..6f81c776973 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -33,7 +33,6 @@ use roc_module::ident::Lowercase; use roc_module::ident::ModuleName; use roc_module::ident::QualifiedModuleName; use roc_module::symbol::IdentId; -use roc_module::symbol::LookedupSymbol; use roc_module::symbol::ModuleId; use roc_module::symbol::Symbol; use roc_parse::ast; @@ -505,6 +504,16 @@ fn canonicalize_alias<'a>( } } +#[macro_export] +macro_rules! params_in_abilities_unimplemented { + ($lookup:expr) => { + match $lookup.params { + None => $lookup.symbol, + Some(_) => unimplemented!("params in abilities"), + } + }; +} + /// Canonicalizes a claimed ability implementation like `{ eq }` or `{ eq: myEq }`. /// Returns a mapping of the ability member to the implementation symbol. /// If there was an error, a problem will be recorded and nothing is returned. @@ -521,22 +530,19 @@ fn canonicalize_claimed_ability_impl<'a>( let label_str = label.value; let region = label.region; - let LookedupSymbol { - symbol: member_symbol, - // todo(agus): params in abilities? - params: _, - } = match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) { - Ok(symbol) => symbol, - Err(_) => { - env.problem(Problem::NotAnAbilityMember { - ability, - name: label_str.to_owned(), - region, - }); + let member_symbol = + match env.qualified_lookup_with_module_id(scope, ability_home, label_str, region) { + Ok(lookup) => params_in_abilities_unimplemented!(lookup), + Err(_) => { + env.problem(Problem::NotAnAbilityMember { + ability, + name: label_str.to_owned(), + region, + }); - return Err(()); - } - }; + return Err(()); + } + }; // There are two options for how the implementation symbol is defined. // @@ -569,9 +575,13 @@ fn canonicalize_claimed_ability_impl<'a>( // To handle both cases, try checking for a shadow first, then check for a direct // reference. We want to check for a direct reference second so that if there is a // shadow, we won't accidentally grab the imported symbol. - let opt_impl_symbol = (scope.lookup_ability_member_shadow(member_symbol)) - // todo(agus): params in abilities? - .or_else(|| scope.lookup_str(label_str, region).map(|s| s.symbol).ok()); + let opt_impl_symbol = + (scope.lookup_ability_member_shadow(member_symbol)).or_else(|| { + scope + .lookup_str(label_str, region) + .map(|s| params_in_abilities_unimplemented!(s)) + .ok() + }); match opt_impl_symbol { // It's possible that even if we find a symbol it is still only the member @@ -617,17 +627,13 @@ fn canonicalize_claimed_ability_impl<'a>( }; let impl_region = value.region; - let LookedupSymbol { - symbol: member_symbol, - // todo(agus): params in abilities? - params: _, - } = match env.qualified_lookup_with_module_id( + let member_symbol = match env.qualified_lookup_with_module_id( scope, ability_home, label.value, label.region, ) { - Ok(symbol) => symbol, + Ok(lookup) => params_in_abilities_unimplemented!(lookup), Err(_) => { env.problem(Problem::NotAnAbilityMember { ability, @@ -638,12 +644,8 @@ fn canonicalize_claimed_ability_impl<'a>( } }; - let LookedupSymbol { - symbol: impl_symbol, - // todo(agus): params in abilities? - params: _, - } = match scope.lookup(&impl_ident.into(), impl_region) { - Ok(symbol) => symbol, + let impl_symbol = match scope.lookup(&impl_ident.into(), impl_region) { + Ok(symbol) => params_in_abilities_unimplemented!(symbol), Err(err) => { env.problem(Problem::RuntimeError(err)); return Err(()); diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 33b2acfb5e2..b49d40c198e 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -7,6 +7,7 @@ use crate::num::{ finish_parsing_base, finish_parsing_float, finish_parsing_num, float_expr_from_result, int_expr_from_result, num_expr_from_result, FloatBound, IntBound, NumBound, }; +use crate::params_in_abilities_unimplemented; use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows}; use crate::procedure::{QualifiedReference, References}; use crate::scope::Scope; @@ -1881,8 +1882,7 @@ fn canonicalize_var_lookup( if scope.abilities_store.is_ability_member_name(lookup.symbol) { AbilityMember( - // todo(agus): params for abilities? - lookup.symbol, + params_in_abilities_unimplemented!(lookup), Some(scope.abilities_store.fresh_specialization_id()), var_store.fresh(), ) @@ -1906,9 +1906,8 @@ fn canonicalize_var_lookup( .insert_value_lookup(lookup.symbol, QualifiedReference::Qualified); if scope.abilities_store.is_ability_member_name(lookup.symbol) { - // todo(agus): params for abilities? AbilityMember( - lookup.symbol, + params_in_abilities_unimplemented!(lookup), Some(scope.abilities_store.fresh_specialization_id()), var_store.fresh(), ) From bc6a84a2157da944157242a6bbe986f3c35d04a3 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Mon, 1 Jul 2024 20:28:36 -0300 Subject: [PATCH 024/203] Report unexpected params --- crates/compiler/can/src/def.rs | 16 ++++++ crates/compiler/can/src/env.rs | 4 ++ crates/compiler/can/src/module.rs | 2 + crates/compiler/can/tests/helpers/mod.rs | 2 + crates/compiler/load/tests/helpers/mod.rs | 1 + crates/compiler/load_internal/src/file.rs | 11 +++++ .../compiler/load_internal/tests/test_load.rs | 46 +++++++++++++++++ crates/compiler/problem/src/can.rs | 14 +++++- crates/compiler/solve/src/solve.rs | 49 ++++++++++--------- crates/reporting/src/error/canonicalize.rs | 16 ++++++ 10 files changed, 137 insertions(+), 24 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 6f81c776973..6d15aeb9a3c 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -3173,6 +3173,22 @@ fn to_pending_value_def<'a>( } }); + match (module_import.params, env.modules_expecting_params.contains(&module_id)) { + (None, true) => { + env.problems.push(Problem::MissingParams { + module_id, + region, + }); + } + (Some(import_params), false) => { + env.problems.push(Problem::UnexpectedParams { + module_id, + region: import_params.params.region, + }); + } + (None, false) | (Some(_), true) => { /* All good */} + } + if let Err(existing_import) = scope .modules diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 880e1c6753c..6d15bf4098e 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -24,6 +24,8 @@ pub struct Env<'a> { pub qualified_module_ids: &'a PackageModuleIds<'a>, + pub modules_expecting_params: VecSet, + /// Problems we've encountered along the way, which will be reported to the user at the end. pub problems: Vec, @@ -52,6 +54,7 @@ impl<'a> Env<'a> { home: ModuleId, module_path: &'a Path, dep_idents: &'a IdentIdsByModule, + modules_expecting_params: VecSet, qualified_module_ids: &'a PackageModuleIds<'a>, opt_shorthand: Option<&'a str>, ) -> Env<'a> { @@ -60,6 +63,7 @@ impl<'a> Env<'a> { home, module_path, dep_idents, + modules_expecting_params, qualified_module_ids, problems: Vec::new(), closures: MutMap::default(), diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index bd602f8516d..888148a9e89 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -286,6 +286,7 @@ pub fn canonicalize_module_defs<'a>( qualified_module_ids: &'a PackageModuleIds<'a>, exposed_ident_ids: IdentIds, dep_idents: &'a IdentIdsByModule, + modules_expecting_params: VecSet, aliases: MutMap, imported_abilities_state: PendingAbilitiesStore, initial_scope: MutMap, @@ -311,6 +312,7 @@ pub fn canonicalize_module_defs<'a>( home, arena.alloc(Path::new(module_path)), dep_idents, + modules_expecting_params, qualified_module_ids, opt_shorthand, ); diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index 508d84ea841..1b81ababc5d 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -80,11 +80,13 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut ); let dep_idents = IdentIds::exposed_builtins(0); + let modules_expecting_params = Default::default(); let mut env = Env::new( arena, home, Path::new("Test.roc"), &dep_idents, + modules_expecting_params, &qualified_module_ids, None, ); diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 63679c98208..7b8ab16d034 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -189,6 +189,7 @@ pub fn can_expr_with<'a>( home, Path::new("Test.roc"), &dep_idents, + Default::default(), &module_ids, None, ); diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index f000bf61c23..0b73291e94b 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -250,6 +250,7 @@ fn start_phase<'a>( .clone(); let mut aliases = MutMap::default(); + let mut modules_expecting_params = VecSet::default(); let mut abilities_store = PendingAbilitiesStore::default(); for imported in parsed.available_modules.keys() { @@ -293,6 +294,10 @@ fn start_phase<'a>( .union(import_store.closure_from_imported(exposed_symbols)); } } + + if state.module_cache.param_patterns.contains_key(imported) { + modules_expecting_params.insert(*imported); + } } let skip_constraint_gen = { @@ -304,6 +309,7 @@ fn start_phase<'a>( BuildTask::CanonicalizeAndConstrain { parsed, dep_idents, + modules_expecting_params, exposed_symbols, qualified_module_ids, aliases, @@ -899,6 +905,7 @@ enum BuildTask<'a> { CanonicalizeAndConstrain { parsed: ParsedModule<'a>, qualified_module_ids: PackageModuleIds<'a>, + modules_expecting_params: VecSet, dep_idents: IdentIdsByModule, exposed_symbols: VecSet, aliases: MutMap, @@ -5018,6 +5025,7 @@ fn canonicalize_and_constrain<'a>( arena: &'a Bump, qualified_module_ids: &'a PackageModuleIds<'a>, dep_idents: IdentIdsByModule, + modules_expecting_params: VecSet, exposed_symbols: VecSet, aliases: MutMap, imported_abilities_state: PendingAbilitiesStore, @@ -5060,6 +5068,7 @@ fn canonicalize_and_constrain<'a>( qualified_module_ids, exposed_ident_ids, &dep_idents, + modules_expecting_params, aliases, imported_abilities_state, initial_scope, @@ -6222,6 +6231,7 @@ fn run_task<'a>( parsed, qualified_module_ids, dep_idents, + modules_expecting_params, exposed_symbols, aliases, abilities_store, @@ -6232,6 +6242,7 @@ fn run_task<'a>( arena, &qualified_module_ids, dep_idents, + modules_expecting_params, exposed_symbols, aliases, abilities_store, diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 0161fdc7e80..6de1cd39329 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -1689,6 +1689,52 @@ fn module_params_typecheck_extra_fields() { // todo(agus): test reporting } +#[test] +fn module_params_unexpected() { + let modules = vec![ + ( + "Api.roc", + indoc!( + r#" + module [url] + + url = "example.com" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api { key: 123 } + + example = Api.url + "# + ), + ), + ]; + + let err = multiple_modules("module_params_unexpected", modules).unwrap_err(); + assert_eq!( + err, + indoc!( + r#" + ── UNEXPECTED PARAMS in tmp/module_params_unexpected/Main.roc ────────────────── + + This import specifies module params: + + 3│ import Api { key: 123 } + ^^^^^^^^^^^^ + + However, Api does not expect any. Did you intend to import a different + module? + "# + ) + ) +} + #[test] fn issue_2863_module_type_does_not_exist() { let modules = vec![ diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index c935d230496..4f2db34a302 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -236,6 +236,14 @@ pub enum Problem { one_occurrence: Region, kind: AliasKind, }, + MissingParams { + module_id: ModuleId, + region: Region, + }, + UnexpectedParams { + module_id: ModuleId, + region: Region, + }, } impl Problem { @@ -308,6 +316,8 @@ impl Problem { Problem::OverAppliedCrash { .. } => RuntimeError, Problem::DefsOnlyUsedInRecursion(_, _) => Warning, Problem::FileProblem { .. } => Fatal, + Problem::MissingParams { .. } => Warning, + Problem::UnexpectedParams { .. } => Warning, } } @@ -464,7 +474,9 @@ impl Problem { | Problem::UnnecessaryOutputWildcard { region } | Problem::OverAppliedCrash { region } | Problem::UnappliedCrash { region } - | Problem::DefsOnlyUsedInRecursion(_, region) => Some(*region), + | Problem::DefsOnlyUsedInRecursion(_, region) + | Problem::MissingParams { region, .. } + | Problem::UnexpectedParams { region, .. } => Some(*region), Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries)) | Problem::BadRecursion(cycle_entries) => { cycle_entries.first().map(|entry| entry.expr_region) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 358a5d5f621..1f9f8177dea 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1413,33 +1413,36 @@ fn solve( *type_index, ); - let expected = module_params_vars - .get(module_id) - // todo(agus): Module has no params? handle - .unwrap(); - - match unify( - &mut env.uenv(), - actual, - *expected, - UnificationMode::EQ, - Polarity::OF_VALUE, - ) { - Success { - vars, - must_implement_ability: _, - lambda_sets_to_specialize: _, - extra_metadata: _, - } => { - env.introduce(rank, &vars); - + match module_params_vars.get(module_id) { + None => { + // Module does not expect params. This will be reported by can. state } + Some(expected) => { + match unify( + &mut env.uenv(), + actual, + *expected, + UnificationMode::EQ, + Polarity::OF_VALUE, + ) { + Success { + vars, + must_implement_ability: _, + lambda_sets_to_specialize: _, + extra_metadata: _, + } => { + env.introduce(rank, &vars); - Failure(vars, _actual_type, _expected_type, _) => { - env.introduce(rank, &vars); + state + } - todo!("report import params mismatch") + Failure(vars, _actual_type, _expected_type, _) => { + env.introduce(rank, &vars); + + todo!("report import params mismatch") + } + } } } } diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 4d7bf9774b8..2b0a695a267 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -64,6 +64,7 @@ const ABILITY_IMPLEMENTATION_NOT_IDENTIFIER: &str = "ABILITY IMPLEMENTATION NOT const DUPLICATE_IMPLEMENTATION: &str = "DUPLICATE IMPLEMENTATION"; const UNNECESSARY_IMPLEMENTATIONS: &str = "UNNECESSARY IMPLEMENTATIONS"; const INCOMPLETE_ABILITY_IMPLEMENTATION: &str = "INCOMPLETE ABILITY IMPLEMENTATION"; +const UNEXPECTED_PARAMS: &str = "UNEXPECTED PARAMS"; pub fn can_problem<'b>( alloc: &'b RocDocAllocator<'b>, @@ -1312,6 +1313,21 @@ pub fn can_problem<'b>( doc = report.doc; title = report.title; } + Problem::MissingParams { .. } => todo!("agus"), + Problem::UnexpectedParams { module_id, region } => { + doc = alloc.stack([ + alloc.reflow("This import specifies module params:"), + alloc.region(lines.convert_region(region), severity), + alloc.concat([ + alloc.reflow("However, "), + alloc.module(module_id), + alloc.reflow( + " does not expect any. Did you intend to import a different module?", + ), + ]), + ]); + title = UNEXPECTED_PARAMS.to_string(); + } }; Report { From 922b1c44ef09868ee8ef3ecc3cecf6999224659c Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 02:59:39 -0300 Subject: [PATCH 025/203] Report missing params --- crates/compiler/can/src/constraint.rs | 6 +- crates/compiler/can/src/copy.rs | 1 + crates/compiler/can/src/debug/pretty_print.rs | 3 + crates/compiler/can/src/def.rs | 142 ++++++++++-------- crates/compiler/can/src/expr.rs | 6 + crates/compiler/can/src/module.rs | 1 + crates/compiler/can/src/traverse.rs | 1 + crates/compiler/constrain/src/expr.rs | 6 +- crates/compiler/load/tests/test_reporting.rs | 21 +++ .../compiler/load_internal/tests/test_load.rs | 98 +++++++++++- crates/compiler/mono/src/ir.rs | 1 + crates/compiler/problem/src/can.rs | 6 - crates/compiler/solve/src/solve.rs | 46 +++--- crates/compiler/solve_problem/src/lib.rs | 14 +- crates/reporting/src/error/canonicalize.rs | 1 - crates/reporting/src/error/type.rs | 26 ++++ 16 files changed, 286 insertions(+), 93 deletions(-) diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index e4fa841d88b..e10815bc874 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -689,11 +689,11 @@ impl Constraints { pub fn import_params( &mut self, - type_index: TypeOrVar, + opt_type_index: Option, module_id: ModuleId, region: Region, ) -> Constraint { - Constraint::ImportParams(type_index, module_id, region) + Constraint::ImportParams(opt_type_index, module_id, region) } } @@ -797,7 +797,7 @@ pub enum Constraint { CheckCycle(Index, IllegalCycleMark), IngestedFile(TypeOrVar, Box, Arc>), - ImportParams(TypeOrVar, ModuleId, Region), + ImportParams(Option, ModuleId, Region), } #[derive(Debug, Clone, Copy, Default)] diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index 41f27b53b9d..dbf4fb79350 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -302,6 +302,7 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr sub!(*var), *module_id, ), + MissingImportParams(module_id, region) => MissingImportParams(*module_id, *region), &AbilityMember(sym, specialization, specialization_var) => { AbilityMember(sym, specialization, sub!(specialization_var)) } diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index bb898737a4b..83edf1ca5f4 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -210,6 +210,9 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, pp_sym(c, f, *sym) } ImportParams(loc_expr, _, _) => expr(c, p, f, &loc_expr.value), + MissingImportParams(module_id, _) => { + text!(f, "", module_id) + } When { loc_cond, branches, .. } => maybe_paren!( diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 6d15aeb9a3c..bca6db9fffd 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -171,7 +171,7 @@ enum PendingValueDef<'a> { symbol: Symbol, loc_pattern: Loc, module_id: ModuleId, - params: ast::Collection<'a, Loc>>>, + opt_provided: Option>>>>, }, /// Ingested file IngestedFile( @@ -191,7 +191,7 @@ impl PendingValueDef<'_> { loc_pattern, symbol: _, module_id: _, - params: _, + opt_provided: _, } => loc_pattern, PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern, } @@ -1141,25 +1141,32 @@ fn canonicalize_value_defs<'a>( PendingValue::ExpectFx(pending_expect) => { pending_expect_fx.push(pending_expect); } - PendingValue::ModuleImport { + PendingValue::ModuleImport(PendingModuleImport { module_id, region, exposed_symbols, params, - } => { + }) => { imports_introduced.push(IntroducedImport { module_id, region, exposed_symbols, }); - if let Some((symbol, loc_pattern, params)) = params { - pending_value_defs.push(PendingValueDef::ImportParams { + match params { + PendingModuleImportParams::None => {} + PendingModuleImportParams::Required { symbol, loc_pattern, - module_id, - params, - }); + opt_provided, + } => { + pending_value_defs.push(PendingValueDef::ImportParams { + symbol, + loc_pattern, + module_id, + opt_provided, + }); + } } } PendingValue::InvalidIngestedFile => { /* skip */ } @@ -2406,26 +2413,36 @@ fn canonicalize_pending_value_def<'a>( symbol, loc_pattern, module_id, - params, + opt_provided, } => { - let (record, can_output) = - canonicalize_record(env, var_store, scope, loc_pattern.region, params); - // Insert a reference to the record so that we don't report it as unused // If the whole module is unused, we'll report that separately output .references .insert_value_lookup(symbol, QualifiedReference::Unqualified); - let references = DefReferences::Value(can_output.references.clone()); - output.union(can_output); + let (expr, references) = match opt_provided { + Some(params) => { + let (record, can_output) = + canonicalize_record(env, var_store, scope, loc_pattern.region, params); - let loc_record = Loc::at(loc_pattern.region, record); + let references = can_output.references.clone(); + output.union(can_output); - let loc_expr = Loc::at( - loc_pattern.region, - Expr::ImportParams(Box::new(loc_record), var_store.fresh(), module_id), - ); + let loc_record = Loc::at(loc_pattern.region, record); + + let expr = + Expr::ImportParams(Box::new(loc_record), var_store.fresh(), module_id); + + (expr, references) + } + None => ( + Expr::MissingImportParams(module_id, loc_pattern.region), + References::new(), + ), + }; + + let loc_expr = Loc::at(loc_pattern.region, expr); let def = single_can_def( loc_pattern, @@ -2437,7 +2454,7 @@ fn canonicalize_pending_value_def<'a>( DefOutput { output, - references, + references: DefReferences::Value(references), def, } } @@ -2982,16 +2999,7 @@ enum PendingValue<'a> { Dbg(PendingExpectOrDbg<'a>), Expect(PendingExpectOrDbg<'a>), ExpectFx(PendingExpectOrDbg<'a>), - ModuleImport { - module_id: ModuleId, - region: Region, - exposed_symbols: Vec<(Symbol, Region)>, - params: Option<( - Symbol, - Loc, - ast::Collection<'a, Loc>>>, - )>, - }, + ModuleImport(PendingModuleImport<'a>), SignatureDefMismatch, InvalidIngestedFile, ImportNameConflict, @@ -3002,6 +3010,24 @@ struct PendingExpectOrDbg<'a> { preceding_comment: Region, } +struct PendingModuleImport<'a> { + module_id: ModuleId, + region: Region, + exposed_symbols: Vec<(Symbol, Region)>, + params: PendingModuleImportParams<'a>, +} + +enum PendingModuleImportParams<'a> { + /// The module does not require any params + None, + /// The module requires params, they may or may not have been provided + Required { + symbol: Symbol, + loc_pattern: Loc, + opt_provided: Option>>>>, + }, +} + pub struct IntroducedImport { module_id: ModuleId, region: Region, @@ -3152,42 +3178,40 @@ fn to_pending_value_def<'a>( None => module_name.clone(), }; - let mut params_sym = None; - - let params = module_import.params.and_then(|params| { + let params = if env.modules_expecting_params.contains(&module_id) { let name_str = name_with_alias.as_str(); + let sym_region = module_import.params.map(|p| p.params.region).unwrap_or(region); - let params_region = params.params.region; - - match scope.introduce_str(format!("#{name_str}").as_str(), params_region) { - Ok(sym) => { - params_sym = Some(sym); + match scope.introduce_str(format!("#{name_str}").as_str(), sym_region) { + Ok(symbol) => { + let loc_pattern = Loc::at(sym_region, Pattern::Identifier(symbol)); - let loc_pattern = Loc::at(params_region, Pattern::Identifier(sym)); - Some((sym, loc_pattern, params.params.value)) + PendingModuleImportParams::Required { + symbol, + loc_pattern, + opt_provided: module_import.params.map(|p| p.params.value), + } }, - Err(_) => { + Err(_) => { // Ignore conflict, it will be handled as a duplicate import - None - } - } - }); - - match (module_import.params, env.modules_expecting_params.contains(&module_id)) { - (None, true) => { - env.problems.push(Problem::MissingParams { - module_id, - region, - }); + PendingModuleImportParams::None + }, } - (Some(import_params), false) => { + } else { + if let Some(params) = module_import.params { env.problems.push(Problem::UnexpectedParams { module_id, - region: import_params.params.region, + region: params.params.region, }); } - (None, false) | (Some(_), true) => { /* All good */} - } + + PendingModuleImportParams::None + }; + + let params_sym = match params { + PendingModuleImportParams::Required { symbol, .. } => Some(symbol), + PendingModuleImportParams::None => None, + }; if let Err(existing_import) = scope @@ -3257,12 +3281,12 @@ fn to_pending_value_def<'a>( } } - PendingValue::ModuleImport { + PendingValue::ModuleImport(PendingModuleImport { module_id, region, exposed_symbols, params, - } + }) } IngestedFileImport(ingested_file) => { let loc_name = ingested_file.name.item; diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index b49d40c198e..10fdd4e480c 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -186,6 +186,9 @@ pub enum Expr { /// Module params expression in import ImportParams(Box>, Variable, ModuleId), + /// The imported module requires params but none were provided + /// We delay this error until solve, so we can report the expected type + MissingImportParams(ModuleId, Region), /// The "crash" keyword Crash { @@ -340,6 +343,7 @@ impl Expr { Self::TupleAccess { index, .. } => Category::TupleAccess(*index), Self::RecordUpdate { .. } => Category::Record, Self::ImportParams(loc_expr, _, _) => loc_expr.value.category(), + Self::MissingImportParams(_, _) => Category::Unknown, Self::Tag { name, arguments, .. } => Category::TagApply { @@ -1966,6 +1970,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { | other @ TypedHole { .. } | other @ ForeignCall { .. } | other @ OpaqueWrapFunction(_) + | other @ MissingImportParams(_, _) | other @ Crash { .. } => other, List { @@ -3270,6 +3275,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec { | Expr::EmptyRecord | Expr::TypedHole(_) | Expr::RuntimeError(_) + | Expr::MissingImportParams(_, _) | Expr::OpaqueWrapFunction(_) => {} } } diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 888148a9e89..2cb3aa3af8a 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -1149,6 +1149,7 @@ fn fix_values_captured_in_closure_expr( | TypedHole { .. } | RuntimeError(_) | ZeroArgumentTag { .. } + | MissingImportParams(_, _) | RecordAccessor { .. } => {} List { loc_elems, .. } => { diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index 3809ab57c23..ecc96ed2f86 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -404,6 +404,7 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { } Expr::TypedHole(_) => { /* terminal */ } Expr::RuntimeError(..) => { /* terminal */ } + Expr::MissingImportParams(..) => { /* terminal */ } } } diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 3e27dc9941f..e3622b3326b 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -595,10 +595,13 @@ pub fn constrain_expr( ¶ms.value, expected_params, ); - let params_con = constraints.import_params(index, *module_id, params.region); + let params_con = constraints.import_params(Some(index), *module_id, params.region); let expr_and_params = constraints.and_constraint([expr_con, params_con]); constraints.exists([*var], expr_and_params) } + MissingImportParams(module_id, region) => { + constraints.import_params(None, *module_id, *region) + } &AbilityMember(symbol, specialization_id, specialization_var) => { // Save the expectation in the `specialization_var` so we know what to specialize, then // lookup the member in the environment. @@ -4136,6 +4139,7 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool { | ExpectFx { .. } | Dbg { .. } | TypedHole(_) + | MissingImportParams(_, _) | RuntimeError(..) | ZeroArgumentTag { .. } | Tag { .. } diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 781f48eba45..c4b99e2447b 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -5005,6 +5005,27 @@ mod test_reporting { "### ); + test_report!( + unexpected_module_params, + indoc!( + r#" + import Dict { key: "abc" } exposing [empty] + + empty {} + "# + ),@r###" + ── UNEXPECTED PARAMS in /code/proj/Main.roc ──────────────────────────────────── + + This import specifies module params: + + 4│ import Dict { key: "abc" } exposing [empty] + ^^^^^^^^^^^^^^ + + However, Dict does not expect any. Did you intend to import a + different module? + "### + ); + test_report!( unfinished_import_as_or_exposing, indoc!( diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 6de1cd39329..ccd8f6b7187 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -27,10 +27,11 @@ use roc_module::symbol::{Interns, ModuleId}; use roc_packaging::cache::RocCacheDir; use roc_problem::can::Problem; use roc_region::all::LineInfo; -use roc_reporting::report::RocDocAllocator; use roc_reporting::report::{can_problem, DEFAULT_PALETTE}; use roc_reporting::report::{strip_colors, RenderTarget}; +use roc_reporting::report::{type_problem, RocDocAllocator}; use roc_solve::FunctionKind; +use roc_solve_problem::TypeError; use roc_target::Target; use roc_test_utils_dir::TmpDir; use roc_types::pretty_print::name_and_print_var; @@ -107,6 +108,33 @@ fn format_can_problems( buf } +fn format_type_problems( + problems: Vec, + home: ModuleId, + interns: &Interns, + filename: PathBuf, + src: &str, +) -> String { + use ven_pretty::DocAllocator; + + let src_lines: Vec<&str> = src.split('\n').collect(); + let lines = LineInfo::new(src); + let alloc = RocDocAllocator::new(&src_lines, home, interns); + let reports = problems + .into_iter() + .flat_map(|problem| type_problem(&alloc, &lines, filename.clone(), problem)) + .map(|report| report.pretty(&alloc)); + + let mut buf = String::new(); + alloc + .stack(reports) + .append(alloc.line()) + .1 + .render_raw(70, &mut roc_reporting::report::CiWrite::new(&mut buf)) + .unwrap(); + buf +} + fn multiple_modules(subdir: &str, files: Vec<(&str, &str)>) -> Result { let arena = Bump::new(); let arena = &arena; @@ -130,11 +158,19 @@ fn multiple_modules(subdir: &str, files: Vec<(&str, &str)>) -> Result [url] + + url = "example.com/$(key)?exp=$(Num.toStr exp)" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api + + example = Api.url + "# + ), + ), + ]; + + let err = multiple_modules("module_params_missing", modules).unwrap_err(); + assert_eq!( + err, + indoc!( + r#" + ── MISSING PARAMS in tmp/module_params_missing/Main.roc ──────────────────────── + + This import specifies no module params: + + 3│ import Api + ^^^^^^^^^^ + + However, Api expects the following to be provided: + + { + exp : Num *, + key : Str, + } + + You can provide params after the module name, like: + + import Menu { echo, read } + "# + ) + ) +} + #[test] fn issue_2863_module_type_does_not_exist() { let modules = vec![ diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 3f036a5c006..364b92f4dd3 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -5883,6 +5883,7 @@ pub fn with_hole<'a>( } TypedHole(_) => runtime_error(env, "Hit a blank"), RuntimeError(e) => runtime_error(env, env.arena.alloc(e.runtime_message())), + MissingImportParams(_, _) => runtime_error(env, env.arena.alloc("Missing import params")), Crash { msg, ret_var: _ } => { let msg_sym = possible_reuse_symbol_or_specialize( env, diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 4f2db34a302..6e127c47e2e 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -236,10 +236,6 @@ pub enum Problem { one_occurrence: Region, kind: AliasKind, }, - MissingParams { - module_id: ModuleId, - region: Region, - }, UnexpectedParams { module_id: ModuleId, region: Region, @@ -316,7 +312,6 @@ impl Problem { Problem::OverAppliedCrash { .. } => RuntimeError, Problem::DefsOnlyUsedInRecursion(_, _) => Warning, Problem::FileProblem { .. } => Fatal, - Problem::MissingParams { .. } => Warning, Problem::UnexpectedParams { .. } => Warning, } } @@ -475,7 +470,6 @@ impl Problem { | Problem::OverAppliedCrash { region } | Problem::UnappliedCrash { region } | Problem::DefsOnlyUsedInRecursion(_, region) - | Problem::MissingParams { region, .. } | Problem::UnexpectedParams { region, .. } => Some(*region), Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries)) | Problem::BadRecursion(cycle_entries) => { diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 1f9f8177dea..81cf46ca4ec 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1401,24 +1401,20 @@ fn solve( } } } - ImportParams(type_index, module_id, _region) => { - let actual = either_type_index_to_var( - env, - rank, - problems, - abilities_store, - obligation_cache, - &mut can_types, - aliases, - *type_index, - ); + ImportParams(opt_provided, module_id, region) => { + match (module_params_vars.get(module_id), opt_provided) { + (Some(expected), Some(provided)) => { + let actual = either_type_index_to_var( + env, + rank, + problems, + abilities_store, + obligation_cache, + &mut can_types, + aliases, + *provided, + ); - match module_params_vars.get(module_id) { - None => { - // Module does not expect params. This will be reported by can. - state - } - Some(expected) => { match unify( &mut env.uenv(), actual, @@ -1444,6 +1440,22 @@ fn solve( } } } + (Some(expected), None) => { + let err_type = env.uenv().var_to_error_type(*expected, Polarity::Neg); + + problems.push(TypeError::MissingImportParams { + module_id: *module_id, + region: *region, + expected: err_type, + }); + + state + } + (None, Some(_)) | (None, None) => { + // Module does not expect params. + // If provided still, canonicalization will produce a warning. + state + } } } }; diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 43de9204c46..01a47106bcd 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -2,7 +2,10 @@ use std::{path::PathBuf, str::Utf8Error}; use roc_can::expected::{Expected, PExpected}; -use roc_module::{ident::Lowercase, symbol::Symbol}; +use roc_module::{ + ident::Lowercase, + symbol::{ModuleId, Symbol}, +}; use roc_problem::{can::CycleEntry, Severity}; use roc_region::all::Region; @@ -33,6 +36,11 @@ pub enum TypeError { }, IngestedFileBadUtf8(Box, Utf8Error), IngestedFileUnsupportedType(Box, ErrorType), + MissingImportParams { + module_id: ModuleId, + region: Region, + expected: ErrorType, + }, } impl TypeError { @@ -52,6 +60,7 @@ impl TypeError { TypeError::Exhaustive(exhtv) => exhtv.severity(), TypeError::StructuralSpecialization { .. } => RuntimeError, TypeError::WrongSpecialization { .. } => RuntimeError, + TypeError::MissingImportParams { .. } => RuntimeError, TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, } @@ -66,7 +75,8 @@ impl TypeError { | TypeError::BadExprMissingAbility(region, ..) | TypeError::StructuralSpecialization { region, .. } | TypeError::WrongSpecialization { region, .. } - | TypeError::BadPatternMissingAbility(region, ..) => Some(*region), + | TypeError::BadPatternMissingAbility(region, ..) + | TypeError::MissingImportParams { region, .. } => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), TypeError::Exhaustive(e) => Some(e.region()), TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region), diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 2b0a695a267..67841b87a8f 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1313,7 +1313,6 @@ pub fn can_problem<'b>( doc = report.doc; title = report.title; } - Problem::MissingParams { .. } => todo!("agus"), Problem::UnexpectedParams { module_id, region } => { doc = alloc.stack([ alloc.reflow("This import specifies module params:"), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index da4bb0ed098..db7269147d6 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -247,6 +247,32 @@ pub fn type_problem<'b>( severity, }) } + MissingImportParams { + module_id, + region, + expected, + } => { + let stack = [ + alloc.reflow("This import specifies no module params:"), + alloc.region(lines.convert_region(region), severity), + alloc.concat([ + alloc.reflow("However, "), + alloc.module(module_id), + alloc.reflow(" expects the following to be provided:"), + ]), + alloc.type_block(error_type_to_doc(alloc, expected)), + alloc.reflow("You can provide params after the module name, like:"), + alloc + .parser_suggestion("import Menu { echo, read }") + .indent(4), + ]; + Some(Report { + title: "MISSING PARAMS".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } } } From f56239bd9bd2051a7186736d13764b545eb1ff12 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 02:46:32 -0300 Subject: [PATCH 026/203] Remove unnecessary ref --- crates/compiler/load_internal/src/file.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 0b73291e94b..1c4dafdfeca 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4667,10 +4667,7 @@ fn run_solve_solve( let ExposedModuleTypes { exposed_types_storage_subs: exposed_types, resolved_implementations: _, - } = exposed_for_module - .exposed_by_module - .get(&module_id) - .unwrap(); + } = exposed_for_module.exposed_by_module.get(module_id).unwrap(); let copied_import = exposed_types .storage_subs From f257c526348898cf6063a1d017ec4d7916b3f75d Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 02:52:09 -0300 Subject: [PATCH 027/203] clippy fix: no manual Option::map --- crates/compiler/load_internal/src/file.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 1c4dafdfeca..a2d7193ce55 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4797,10 +4797,10 @@ fn run_solve<'a>( // TODO remove when we write builtins in roc let aliases = module.aliases.clone(); - let opt_params_var = match module.params_pattern { - Some((params_var, _, _)) => Some(params_var), - None => None, - }; + let opt_params_var = module + .params_pattern + .as_ref() + .map(|(params_var, _, _)| *params_var); let mut module = module; let loc_expects = std::mem::take(&mut module.loc_expects); From 400c156d8148f4474e7a2351a6f2664a1829d473 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 02:57:25 -0300 Subject: [PATCH 028/203] Replace params mono todo with unimplemented --- crates/compiler/mono/src/ir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index 364b92f4dd3..cfef124f1a1 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4423,7 +4423,7 @@ pub fn with_hole<'a>( specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol) } ParamsVar { .. } => { - todo!("agus: handle params var") + unimplemented!("module params code generation") } ImportParams(loc_expr, _, _) => with_hole( env, From d2c9953429aa3ba7fc193cc712219a00656c5871 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 03:00:21 -0300 Subject: [PATCH 029/203] Handle import params lambda sets and abilities in solve --- crates/compiler/solve/src/solve.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 81cf46ca4ec..c7f53b17293 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1424,12 +1424,27 @@ fn solve( ) { Success { vars, - must_implement_ability: _, - lambda_sets_to_specialize: _, + must_implement_ability, + lambda_sets_to_specialize, extra_metadata: _, } => { env.introduce(rank, &vars); + problems.extend(obligation_cache.check_obligations( + env.subs, + abilities_store, + must_implement_ability, + AbilityImplError::DoesNotImplement, + )); + compact_lambdas_and_check_obligations( + env, + problems, + abilities_store, + obligation_cache, + awaiting_specializations, + lambda_sets_to_specialize, + ); + state } From 89fc1104f0a6b5d6747a72eb05d6cc5ce0b4dee3 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 10:51:40 -0300 Subject: [PATCH 030/203] Report import params mismatch --- .../compiler/load_internal/tests/test_load.rs | 107 ++++++++++++++++-- crates/compiler/solve/src/solve.rs | 23 ++-- crates/compiler/solve_problem/src/lib.rs | 13 +-- crates/reporting/src/error/type.rs | 33 +++++- 4 files changed, 148 insertions(+), 28 deletions(-) diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index ccd8f6b7187..9be7f65bd9e 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -1662,7 +1662,6 @@ fn module_params_optional() { } #[test] -#[should_panic(expected = "report import params mismatch")] fn module_params_typecheck_fail() { let modules = vec![ ( @@ -1689,13 +1688,85 @@ fn module_params_typecheck_fail() { ), ]; - let _ = multiple_modules("module_params_typecheck_fail", modules); - // todo(agus): test reporting + let result = multiple_modules("module_params_typecheck_fail", modules).unwrap_err(); + assert_eq!( + result, + indoc!( + r#" + ── IMPORT PARAMS MISMATCH in tmp/module_params_typecheck_fail/Main.roc ───────── + + Something is off with the params provided by this import: + + 3│ import Api { key: 123 } + ^^^^^^^^^^^^ + + This is the type I inferred: + + { key : Num * } + + However, Api expects: + + { key : Str } + "# + ) + ); +} + +#[test] +fn module_params_missing_fields() { + let modules = vec![ + ( + "Api.roc", + indoc!( + r#" + module { key } -> [url] + + url = "example.com/$(key)" + "# + ), + ), + ( + "Main.roc", + indoc!( + r#" + module [example] + + import Api {} + + example = Api.url + "# + ), + ), + ]; + + let result = multiple_modules("module_params_missing_fields", modules).unwrap_err(); + assert_eq!( + result, + indoc!( + r#" + ── IMPORT PARAMS MISMATCH in tmp/module_params_missing_fields/Main.roc ───────── + + Something is off with the params provided by this import: + + 3│ import Api {} + ^^ + + This is the type I inferred: + + {} + + However, Api expects: + + { key : Str } + + Tip: Looks like the key field is missing. + "# + ) + ); } #[test] -#[should_panic(expected = "report import params mismatch")] -fn module_params_typecheck_extra_fields() { +fn module_params_extra_fields() { let modules = vec![ ( "Api.roc", @@ -1721,8 +1792,30 @@ fn module_params_typecheck_extra_fields() { ), ]; - let _ = multiple_modules("module_params_typecheck_extra_fields", modules); - // todo(agus): test reporting + let result = multiple_modules("module_params_extra_fields", modules).unwrap_err(); + assert_eq!( + result, + indoc!( + r#" + ── IMPORT PARAMS MISMATCH in tmp/module_params_extra_fields/Main.roc ─────────── + + Something is off with the params provided by this import: + + 3│ import Api { key: "123", doesNotExist: Bool.true } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + This is the type I inferred: + + { doesNotExist : Bool, … } + + However, Api expects: + + { … } + + + "# + ) + ); } #[test] diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index c7f53b17293..9dfd5180114 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1448,21 +1448,28 @@ fn solve( state } - Failure(vars, _actual_type, _expected_type, _) => { + Failure(vars, actual_type, expected_type, _) => { env.introduce(rank, &vars); - todo!("report import params mismatch") + problems.push(TypeError::ImportParamsMismatch( + *region, + *module_id, + actual_type, + expected_type, + )); + + state } } } (Some(expected), None) => { - let err_type = env.uenv().var_to_error_type(*expected, Polarity::Neg); + let expected_type = env.uenv().var_to_error_type(*expected, Polarity::Neg); - problems.push(TypeError::MissingImportParams { - module_id: *module_id, - region: *region, - expected: err_type, - }); + problems.push(TypeError::MissingImportParams( + *region, + *module_id, + expected_type, + )); state } diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 01a47106bcd..df6b8ea28ef 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -36,11 +36,8 @@ pub enum TypeError { }, IngestedFileBadUtf8(Box, Utf8Error), IngestedFileUnsupportedType(Box, ErrorType), - MissingImportParams { - module_id: ModuleId, - region: Region, - expected: ErrorType, - }, + MissingImportParams(Region, ModuleId, ErrorType), + ImportParamsMismatch(Region, ModuleId, ErrorType, ErrorType), } impl TypeError { @@ -60,7 +57,8 @@ impl TypeError { TypeError::Exhaustive(exhtv) => exhtv.severity(), TypeError::StructuralSpecialization { .. } => RuntimeError, TypeError::WrongSpecialization { .. } => RuntimeError, - TypeError::MissingImportParams { .. } => RuntimeError, + TypeError::MissingImportParams(..) => RuntimeError, + TypeError::ImportParamsMismatch(..) => RuntimeError, TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, } @@ -76,7 +74,8 @@ impl TypeError { | TypeError::StructuralSpecialization { region, .. } | TypeError::WrongSpecialization { region, .. } | TypeError::BadPatternMissingAbility(region, ..) - | TypeError::MissingImportParams { region, .. } => Some(*region), + | TypeError::MissingImportParams(region, ..) + | TypeError::ImportParamsMismatch(region, ..) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), TypeError::Exhaustive(e) => Some(e.region()), TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index db7269147d6..84383c5a7e5 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -247,11 +247,7 @@ pub fn type_problem<'b>( severity, }) } - MissingImportParams { - module_id, - region, - expected, - } => { + MissingImportParams(region, module_id, expected) => { let stack = [ alloc.reflow("This import specifies no module params:"), alloc.region(lines.convert_region(region), severity), @@ -273,6 +269,31 @@ pub fn type_problem<'b>( severity, }) } + ImportParamsMismatch(region, module_id, actual_type, expected_type) => { + let stack = [ + alloc.reflow("Something is off with the params provided by this import:"), + alloc.region(lines.convert_region(region), severity), + type_comparison( + alloc, + actual_type, + expected_type, + ExpectationContext::Arbitrary, + alloc.reflow("This is the type I inferred:"), + alloc.concat([ + alloc.reflow("However, "), + alloc.module(module_id), + alloc.reflow(" expects:"), + ]), + None, + ), + ]; + Some(Report { + title: "IMPORT PARAMS MISMATCH".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } } } @@ -1583,7 +1604,7 @@ fn to_expr_report<'b>( Reason::RecordDefaultField(_) => { unimplemented!("record default field is not implemented yet") } - Reason::ImportParams(_) => todo!("agus: report bad params"), + Reason::ImportParams(_) => unreachable!(), }, } } From d23a8dc618125b027dbbf51faf85e0e89130461a Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 2 Jul 2024 22:48:47 -0300 Subject: [PATCH 031/203] Fix importing of module params vars --- crates/compiler/load_internal/src/file.rs | 77 ++++++++--------------- crates/compiler/solve/src/module.rs | 10 ++- crates/compiler/solve/src/solve.rs | 5 +- crates/compiler/test_derive/src/util.rs | 4 +- crates/compiler/types/src/subs.rs | 1 + 5 files changed, 36 insertions(+), 61 deletions(-) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index a2d7193ce55..d0d743d6abf 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -346,21 +346,6 @@ fn start_phase<'a>( None }; - let mut module_params_vars = Vec::with_capacity(available_modules.len()); - - for (module_id, _) in available_modules.iter() { - if let Some((_, _, pat)) = state.module_cache.param_patterns.get(module_id) { - match pat.value { - roc_can::pattern::Pattern::RecordDestructure { whole_var, .. } => { - module_params_vars.push((*module_id, whole_var)); - } - _ => { - internal_error!("Module params can only be records"); - } - } - } - } - BuildTask::solve_module( module, ident_ids, @@ -372,7 +357,6 @@ fn start_phase<'a>( pending_derives, var_store, available_modules, - module_params_vars, &state.exposed_types, dep_idents, declarations, @@ -928,7 +912,6 @@ enum BuildTask<'a> { dep_idents: IdentIdsByModule, cached_subs: CachedTypeState, derived_module: SharedDerivedModule, - module_params_vars: Vec<(ModuleId, Variable)>, #[cfg(debug_assertions)] checkmate: Option, @@ -4325,7 +4308,6 @@ impl<'a> BuildTask<'a> { pending_derives: PendingDerives, var_store: VarStore, imported_modules: MutMap, - module_params_vars: Vec<(ModuleId, Variable)>, exposed_types: &ExposedByModule, dep_idents: IdentIdsByModule, declarations: Declarations, @@ -4354,7 +4336,6 @@ impl<'a> BuildTask<'a> { module_timing, cached_subs, derived_module, - module_params_vars, #[cfg(debug_assertions)] checkmate, @@ -4410,7 +4391,7 @@ pub fn add_imports( def_types: &mut Vec<(Symbol, Loc)>, imported_rigid_vars: &mut Vec, imported_flex_vars: &mut Vec, -) -> (Vec, AbilitiesStore) { +) -> (Vec, VecMap, AbilitiesStore) { let mut import_variables = Vec::new(); let mut cached_symbol_vars = VecMap::default(); @@ -4517,7 +4498,31 @@ pub fn add_imports( }, ); - (import_variables, abilities_store) + let mut imported_param_vars = VecMap::default(); + + for (module_id, _) in exposed_for_module.exposed_by_module.iter_all() { + let ExposedModuleTypes { + exposed_types_storage_subs: exposed_types, + .. + } = exposed_for_module.exposed_by_module.get(module_id).unwrap(); + + if let Some(stored_aprams_var) = exposed_types.stored_params_var { + let copied_import = exposed_types + .storage_subs + .export_variable_to(subs, stored_aprams_var); + + let copied_import_var = extend_imports_data_with_copied_import( + copied_import, + &mut import_variables, + imported_rigid_vars, + imported_flex_vars, + ); + + imported_param_vars.insert(*module_id, copied_import_var); + } + } + + (import_variables, imported_param_vars, abilities_store) } enum OnSymbolNotFound { @@ -4623,7 +4628,6 @@ struct SolveResult { #[allow(clippy::complexity)] fn run_solve_solve( exposed_for_module: ExposedForModule, - original_param_vars: Vec<(ModuleId, Variable)>, mut types: Types, mut constraints: Constraints, constraint: ConstraintSoa, @@ -4650,7 +4654,7 @@ fn run_solve_solve( let mut subs = Subs::new_from_varstore(var_store); - let (mut import_variables, abilities_store) = add_imports( + let (import_variables, imported_param_vars, abilities_store) = add_imports( module.module_id, &mut constraints, &mut subs, @@ -4661,28 +4665,6 @@ fn run_solve_solve( &mut imported_flex_vars, ); - let mut imported_param_vars = HashMap::with_capacity(original_param_vars.len()); - - for (module_id, params_var) in original_param_vars.iter() { - let ExposedModuleTypes { - exposed_types_storage_subs: exposed_types, - resolved_implementations: _, - } = exposed_for_module.exposed_by_module.get(module_id).unwrap(); - - let copied_import = exposed_types - .storage_subs - .export_variable_to(&mut subs, *params_var); - - let copied_import_var = extend_imports_data_with_copied_import( - copied_import, - &mut import_variables, - &mut imported_rigid_vars, - &mut imported_flex_vars, - ); - - imported_param_vars.insert(*module_id, copied_import_var); - } - let actual_constraint = constraints.let_import_constraint( imported_rigid_vars, imported_flex_vars, @@ -4776,7 +4758,6 @@ fn run_solve<'a>( ident_ids: IdentIds, mut module_timing: ModuleTiming, exposed_for_module: ExposedForModule, - module_params_vars: Vec<(ModuleId, Variable)>, types: Types, constraints: Constraints, constraint: ConstraintSoa, @@ -4812,7 +4793,6 @@ fn run_solve<'a>( match cached_types.lock().remove(&module_id) { None => run_solve_solve( exposed_for_module, - module_params_vars, types, constraints, constraint, @@ -4844,7 +4824,6 @@ fn run_solve<'a>( } else { run_solve_solve( exposed_for_module, - module_params_vars, types, constraints, constraint, @@ -6265,7 +6244,6 @@ fn run_task<'a>( dep_idents, cached_subs, derived_module, - module_params_vars, #[cfg(debug_assertions)] checkmate, @@ -6274,7 +6252,6 @@ fn run_task<'a>( ident_ids, module_timing, exposed_for_module, - module_params_vars, types, constraints, constraint, diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 1a140595e9b..de76e494b66 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::solve::RunSolveOutput; use crate::FunctionKind; use crate::{aliases::Aliases, solve}; @@ -85,7 +83,7 @@ pub struct SolveConfig<'a> { /// Module params pub params_pattern: Option, - pub module_params_vars: HashMap, + pub module_params_vars: VecMap, } pub struct SolveOutput { @@ -163,9 +161,8 @@ pub fn exposed_types_storage_subs( stored_vars_by_symbol.insert(*symbol, new_var); } - if let Some(params_var) = params_var { - storage_subs.import_variable_from(subs, params_var); - } + let stored_params_var = + params_var.map(|params_var| storage_subs.import_variable_from(subs, params_var).variable); let mut stored_specialization_lambda_set_vars = VecMap::with_capacity(solved_implementations.len()); @@ -224,6 +221,7 @@ pub fn exposed_types_storage_subs( stored_vars_by_symbol, stored_specialization_lambda_set_vars, stored_ability_member_vars, + stored_params_var, } } diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 9dfd5180114..deb3354babc 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::ability::{ resolve_ability_specialization, type_implementing_specialization, AbilityImplError, CheckedDerives, ObligationCache, PendingDerivesTable, Resolved, @@ -18,6 +16,7 @@ use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo}; use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::{Cycle, LetConstraint, OpportunisticResolve}; use roc_can::expected::{Expected, PExpected}; +use roc_collections::VecMap; use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; @@ -245,7 +244,7 @@ fn solve( obligation_cache: &mut ObligationCache, awaiting_specializations: &mut AwaitingSpecializations, params_pattern: Option, - module_params_vars: HashMap, + module_params_vars: VecMap, ) -> State { let scope = Scope::new(params_pattern); diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 746c152e4c6..a4d78169edf 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -405,7 +405,7 @@ fn check_derived_typechecks_and_golden( let mut def_types = Default::default(); let mut rigid_vars = Default::default(); let mut flex_vars = Default::default(); - let (import_variables, abilities_store) = add_imports( + let (import_variables, imported_param_vars, abilities_store) = add_imports( test_module, &mut constraints, &mut test_subs, @@ -439,7 +439,7 @@ fn check_derived_typechecks_and_golden( exposed_by_module: &exposed_for_module.exposed_by_module, derived_module: Default::default(), params_pattern: None, - module_params_vars: Default::default(), + module_params_vars: imported_param_vars, #[cfg(debug_assertions)] checkmate: None, diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index a14e61a817a..fe22db881f4 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -4518,6 +4518,7 @@ pub struct ExposedTypesStorageSubs { pub stored_specialization_lambda_set_vars: VecMap, /// ability member signature in other module -> var in storage subs pub stored_ability_member_vars: VecMap, + pub stored_params_var: Option, } #[derive(Clone, Debug)] From d775d9ef535606d61ce98e17552fc820079c2d8b Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Thu, 4 Jul 2024 12:25:02 -0300 Subject: [PATCH 032/203] Prevent top-level fns from capturing params --- crates/compiler/can/src/module.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 2cb3aa3af8a..283523ced21 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -411,6 +411,10 @@ pub fn canonicalize_module_defs<'a>( let loc_pattern = Loc::at(pattern.region, can_pattern); + for (symbol, _) in BindingsFromPattern::new(&loc_pattern) { + env.top_level_symbols.insert(symbol); + } + ( var_store.fresh(), AnnotatedMark::new(var_store), From f80cb341a606ff3505f3a9e1a1e58785d0f96d98 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Fri, 5 Jul 2024 21:35:52 -0300 Subject: [PATCH 033/203] Move ScopeModules to can --- crates/compiler/can/src/annotation.rs | 4 +- crates/compiler/can/src/env.rs | 7 +- crates/compiler/can/src/expr.rs | 4 +- crates/compiler/can/src/scope.rs | 132 ++++++++++++++++++- crates/compiler/module/src/symbol.rs | 143 +-------------------- crates/compiler/problem/src/can.rs | 9 +- crates/reporting/src/error/canonicalize.rs | 4 +- 7 files changed, 151 insertions(+), 152 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index c1cc17b2985..692188d1227 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -1,9 +1,9 @@ use crate::env::Env; use crate::procedure::{QualifiedReference, References}; -use crate::scope::{PendingAbilitiesInScope, Scope}; +use crate::scope::{LookedupSymbol, PendingAbilitiesInScope, Scope}; use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet}; use roc_module::ident::{Ident, Lowercase, TagName}; -use roc_module::symbol::{LookedupSymbol, Symbol}; +use roc_module::symbol::Symbol; use roc_parse::ast::{AssignedField, ExtractSpaces, Pattern, Tag, TypeAnnotation, TypeHeader}; use roc_problem::can::ShadowKind; use roc_region::all::{Loc, Region}; diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 6d15bf4098e..b5062c5d972 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -1,14 +1,11 @@ use std::path::Path; use crate::procedure::References; -use crate::scope::Scope; +use crate::scope::{LookedupModule, LookedupSymbol, Scope}; use bumpalo::Bump; use roc_collections::{MutMap, VecSet}; use roc_module::ident::{Ident, ModuleName}; -use roc_module::symbol::{ - IdentIdsByModule, LookedupModule, LookedupSymbol, ModuleId, PQModuleName, PackageModuleIds, - Symbol, -}; +use roc_module::symbol::{IdentIdsByModule, ModuleId, PQModuleName, PackageModuleIds, Symbol}; use roc_problem::can::{Problem, RuntimeError}; use roc_region::all::{Loc, Region}; diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 10fdd4e480c..7a137947944 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -10,7 +10,7 @@ use crate::num::{ use crate::params_in_abilities_unimplemented; use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows}; use crate::procedure::{QualifiedReference, References}; -use crate::scope::Scope; +use crate::scope::{LookedupSymbol, Scope}; use crate::traverse::{walk_expr, Visitor}; use roc_collections::soa::Index; use roc_collections::{SendMap, VecMap, VecSet}; @@ -18,7 +18,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::low_level::LowLevel; -use roc_module::symbol::{LookedupSymbol, ModuleId, Symbol}; +use roc_module::symbol::{ModuleId, Symbol}; use roc_parse::ast::{self, Defs, PrecedenceConflict, StrLiteral}; use roc_parse::ident::Accessor; use roc_parse::pattern::PatternType::*; diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index 4ba1bf6002c..bfc16d2a355 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -1,8 +1,8 @@ use roc_collections::{VecMap, VecSet}; use roc_error_macros::internal_error; use roc_module::ident::{Ident, ModuleName}; -use roc_module::symbol::{IdentId, IdentIds, LookedupSymbol, ModuleId, ScopeModules, Symbol}; -use roc_problem::can::RuntimeError; +use roc_module::symbol::{IdentId, IdentIds, ModuleId, ModuleIds, Symbol}; +use roc_problem::can::{RuntimeError, ScopeModuleSource}; use roc_region::all::{Loc, Region}; use roc_types::subs::Variable; use roc_types::types::{Alias, AliasKind, AliasVar, Type}; @@ -656,6 +656,134 @@ impl ScopedIdentIds { } } +#[derive(Debug, Clone)] +pub struct ScopeModules { + modules: VecMap, + sources: Vec, + params: Vec>, +} + +impl ScopeModules { + pub fn new(home_id: ModuleId, home_name: ModuleName) -> Self { + let builtins = ModuleIds::default(); + let builtins_iter = builtins.iter(); + let count = builtins_iter.len(); + + let mut modules = VecMap::with_capacity(count + 1); + let mut sources = vec![ScopeModuleSource::Builtin; count]; + let mut params = vec![None; count]; + + for (module_id, module_name) in builtins_iter { + modules.insert(module_name.clone(), module_id); + } + + if !home_id.is_builtin() { + modules.insert(home_name, home_id); + sources.push(ScopeModuleSource::Current); + params.push(None); + } + + Self { + modules, + sources, + params, + } + } + + pub fn lookup(&self, module_name: &ModuleName) -> Option { + self.modules + .get_with_index(module_name) + .map(|(index, module_id)| LookedupModule { + id: *module_id, + params: self.params.get(index).copied().unwrap(), + }) + } + + pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option { + self.modules + .get_index_by_value(module_id) + .map(|index| LookedupModule { + id: *module_id, + params: self.params.get(index).copied().unwrap(), + }) + } + + pub fn available_names(&self) -> impl Iterator { + self.modules.keys() + } + + pub fn insert( + &mut self, + module_name: ModuleName, + module_id: ModuleId, + params_symbol: Option, + region: Region, + ) -> Result<(), ScopeModuleSource> { + if let Some((index, existing_module_id)) = self.modules.get_with_index(&module_name) { + if *existing_module_id == module_id { + return Ok(()); + } + + return Err(*self.sources.get(index).unwrap()); + } + + self.modules.insert(module_name, module_id); + self.sources.push(ScopeModuleSource::Import(region)); + self.params.push(params_symbol); + Ok(()) + } + + pub fn len(&self) -> usize { + debug_assert_eq!(self.modules.len(), self.sources.len()); + debug_assert_eq!(self.modules.len(), self.params.len()); + self.modules.len() + } + + pub fn is_empty(&self) -> bool { + debug_assert_eq!(self.modules.is_empty(), self.sources.is_empty()); + debug_assert_eq!(self.modules.is_empty(), self.params.is_empty()); + self.modules.is_empty() + } + + pub fn truncate(&mut self, len: usize) { + self.modules.truncate(len); + self.sources.truncate(len); + self.params.truncate(len); + } +} + +#[derive(Debug, Clone)] +pub struct LookedupSymbol { + pub symbol: Symbol, + pub params: Option, +} + +impl LookedupSymbol { + pub fn new(symbol: Symbol, params: Option) -> Self { + Self { symbol, params } + } + + pub fn no_params(symbol: Symbol) -> Self { + Self::new(symbol, None) + } +} + +pub struct LookedupModule { + pub id: ModuleId, + pub params: Option, +} + +impl LookedupModule { + pub fn into_symbol(&self, symbol: Symbol) -> LookedupSymbol { + debug_assert_eq!(symbol.module_id(), self.id); + + LookedupSymbol { + symbol, + params: self.params, + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index b626c9b273f..8d4a7d98a7c 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -620,114 +620,12 @@ impl ModuleIds { pub fn available_modules(&self) -> impl Iterator { self.by_id.iter() } -} - -#[derive(Debug, Clone)] -pub struct LookedupSymbol { - pub symbol: Symbol, - pub params: Option, -} - -impl LookedupSymbol { - pub fn new(symbol: Symbol, params: Option) -> Self { - Self { symbol, params } - } - - pub fn no_params(symbol: Symbol) -> Self { - Self::new(symbol, None) - } -} - -#[derive(Debug, Clone)] -pub struct ScopeModules { - modules: VecMap, - sources: Vec, - params: Vec>, -} - -pub struct LookedupModule { - pub id: ModuleId, - pub params: Option, -} - -impl LookedupModule { - pub fn into_symbol(&self, symbol: Symbol) -> LookedupSymbol { - debug_assert_eq!(symbol.module_id(), self.id); - - LookedupSymbol { - symbol, - params: self.params, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ScopeModuleSource { - Builtin, - Current, - Import(Region), -} - -impl ScopeModules { - pub fn lookup(&self, module_name: &ModuleName) -> Option { - self.modules - .get_with_index(module_name) - .map(|(index, module_id)| LookedupModule { - id: *module_id, - params: self.params.get(index).copied().unwrap(), - }) - } - - pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option { - self.modules - .get_index_by_value(module_id) - .map(|index| LookedupModule { - id: *module_id, - params: self.params.get(index).copied().unwrap(), - }) - } - - pub fn available_names(&self) -> impl Iterator { - self.modules.keys() - } - - pub fn insert( - &mut self, - module_name: ModuleName, - module_id: ModuleId, - params_symbol: Option, - region: Region, - ) -> Result<(), ScopeModuleSource> { - if let Some((index, existing_module_id)) = self.modules.get_with_index(&module_name) { - if *existing_module_id == module_id { - return Ok(()); - } - return Err(*self.sources.get(index).unwrap()); - } - - self.modules.insert(module_name, module_id); - self.sources.push(ScopeModuleSource::Import(region)); - self.params.push(params_symbol); - Ok(()) - } - - pub fn len(&self) -> usize { - debug_assert_eq!(self.modules.len(), self.sources.len()); - debug_assert_eq!(self.modules.len(), self.params.len()); - self.modules.len() - } - - pub fn is_empty(&self) -> bool { - debug_assert_eq!(self.modules.is_empty(), self.sources.is_empty()); - debug_assert_eq!(self.modules.is_empty(), self.params.is_empty()); - self.modules.is_empty() - } - - pub fn truncate(&mut self, len: usize) { - self.modules.truncate(len); - self.sources.truncate(len); - self.params.truncate(len); + pub fn iter(&self) -> impl ExactSizeIterator { + self.by_id + .iter() + .enumerate() + .map(|(index, name)| (ModuleId::from_zero_indexed(index), name)) } } @@ -1071,37 +969,6 @@ macro_rules! define_builtins { } } - impl ScopeModules { - pub fn new(home_id: ModuleId, home_name: ModuleName) -> Self { - // +1 because the user will be compiling at least 1 non-builtin module! - let capacity = $total + 1; - - let mut modules = VecMap::with_capacity(capacity); - let mut sources = Vec::with_capacity(capacity); - let mut params = Vec::with_capacity(capacity); - - if !home_id.is_builtin() { - modules.insert(home_name, home_id); - sources.push(ScopeModuleSource::Current); - params.push(None); - } - - let mut insert_both = |id: ModuleId, name_str: &'static str| { - let name: ModuleName = name_str.into(); - - modules.insert(name, id); - sources.push(ScopeModuleSource::Builtin); - params.push(None); - }; - - $( - insert_both(ModuleId::$module_const, $module_name); - )+ - - ScopeModules { modules, sources, params } - } - } - impl<'a> Default for PackageModuleIds<'a> { fn default() -> Self { // +1 because the user will be compiling at least 1 non-builtin module! diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 6e127c47e2e..0d3d1284508 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use roc_collections::all::MutSet; use roc_module::called_via::BinOp; use roc_module::ident::{Ident, Lowercase, ModuleName, TagName}; -use roc_module::symbol::{ModuleId, ScopeModuleSource, Symbol}; +use roc_module::symbol::{ModuleId, Symbol}; use roc_parse::ast::Base; use roc_parse::pattern::PatternType; use roc_region::all::{Loc, Region}; @@ -242,6 +242,13 @@ pub enum Problem { }, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ScopeModuleSource { + Builtin, + Current, + Import(Region), +} + impl Problem { pub fn severity(&self) -> Severity { use Severity::{Fatal, RuntimeError, Warning}; diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 67841b87a8f..ce41b1fb3bf 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1,10 +1,10 @@ use roc_collections::all::MutSet; use roc_module::ident::{Ident, Lowercase, ModuleName}; -use roc_module::symbol::{ScopeModuleSource, DERIVABLE_ABILITIES}; +use roc_module::symbol::DERIVABLE_ABILITIES; use roc_problem::can::PrecedenceProblem::BothNonAssociative; use roc_problem::can::{ BadPattern, CycleEntry, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError, - ShadowKind, + ScopeModuleSource, ShadowKind, }; use roc_problem::Severity; use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region}; From 33cbaa5cd8cec212cced654e132eab869e657614 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Fri, 5 Jul 2024 22:05:33 -0300 Subject: [PATCH 034/203] Replace ScopeModules/modules VecMap with 2 Vecs --- crates/compiler/can/src/scope.rs | 64 +++++++++++++--------- crates/compiler/collections/src/vec_map.rs | 13 ----- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index bfc16d2a355..583561519db 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -658,8 +658,13 @@ impl ScopedIdentIds { #[derive(Debug, Clone)] pub struct ScopeModules { - modules: VecMap, + /// The ids of all modules in scope + ids: Vec, + /// The alias or original name of each module in scope + names: Vec, + /// Why is this module in scope? sources: Vec, + /// The params of a module if any params: Vec>, } @@ -669,47 +674,53 @@ impl ScopeModules { let builtins_iter = builtins.iter(); let count = builtins_iter.len(); - let mut modules = VecMap::with_capacity(count + 1); + let mut ids = Vec::with_capacity(count + 1); + let mut names = Vec::with_capacity(count + 1); let mut sources = vec![ScopeModuleSource::Builtin; count]; let mut params = vec![None; count]; for (module_id, module_name) in builtins_iter { - modules.insert(module_name.clone(), module_id); + ids.push(module_id); + names.push(module_name.clone()); } if !home_id.is_builtin() { - modules.insert(home_name, home_id); + ids.push(home_id); + names.push(home_name); sources.push(ScopeModuleSource::Current); params.push(None); } Self { - modules, + ids, + names, sources, params, } } pub fn lookup(&self, module_name: &ModuleName) -> Option { - self.modules - .get_with_index(module_name) - .map(|(index, module_id)| LookedupModule { - id: *module_id, - params: self.params.get(index).copied().unwrap(), + self.names + .iter() + .position(|name| name == module_name) + .map(|index| LookedupModule { + id: self.ids[index], + params: self.params[index], }) } pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option { - self.modules - .get_index_by_value(module_id) + self.ids + .iter() + .position(|id| id == module_id) .map(|index| LookedupModule { - id: *module_id, - params: self.params.get(index).copied().unwrap(), + id: self.ids[index], + params: self.params[index], }) } pub fn available_names(&self) -> impl Iterator { - self.modules.keys() + self.names.iter() } pub fn insert( @@ -719,34 +730,35 @@ impl ScopeModules { params_symbol: Option, region: Region, ) -> Result<(), ScopeModuleSource> { - if let Some((index, existing_module_id)) = self.modules.get_with_index(&module_name) { - if *existing_module_id == module_id { + if let Some(index) = self.names.iter().position(|name| name == &module_name) { + if self.ids[index] == module_id { return Ok(()); } - return Err(*self.sources.get(index).unwrap()); + return Err(self.sources[index]); } - self.modules.insert(module_name, module_id); + self.ids.push(module_id); + self.names.push(module_name); self.sources.push(ScopeModuleSource::Import(region)); self.params.push(params_symbol); Ok(()) } pub fn len(&self) -> usize { - debug_assert_eq!(self.modules.len(), self.sources.len()); - debug_assert_eq!(self.modules.len(), self.params.len()); - self.modules.len() + debug_assert_eq!(self.ids.len(), self.names.len()); + debug_assert_eq!(self.ids.len(), self.sources.len()); + debug_assert_eq!(self.ids.len(), self.params.len()); + self.ids.len() } pub fn is_empty(&self) -> bool { - debug_assert_eq!(self.modules.is_empty(), self.sources.is_empty()); - debug_assert_eq!(self.modules.is_empty(), self.params.is_empty()); - self.modules.is_empty() + self.ids.is_empty() } pub fn truncate(&mut self, len: usize) { - self.modules.truncate(len); + self.ids.truncate(len); + self.names.truncate(len); self.sources.truncate(len); self.params.truncate(len); } diff --git a/crates/compiler/collections/src/vec_map.rs b/crates/compiler/collections/src/vec_map.rs index 9fea256b56a..69ebd1b592d 100644 --- a/crates/compiler/collections/src/vec_map.rs +++ b/crates/compiler/collections/src/vec_map.rs @@ -84,13 +84,6 @@ impl VecMap { } } - pub fn get_with_index(&self, key: &K) -> Option<(usize, &V)> { - self.keys - .iter() - .position(|x| x == key) - .map(|index| (index, &self.values[index])) - } - pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { match self.keys.iter().position(|x| x == key) { None => None, @@ -159,12 +152,6 @@ impl VecMap { } } -impl VecMap { - pub fn get_index_by_value(&self, value: &V) -> Option { - self.values.iter().position(|x| x == value) - } -} - impl Extend<(K, V)> for VecMap { #[inline(always)] fn extend>(&mut self, iter: T) { From 63e722f61d258eaa12493aa855de3c43d0e6d29f Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 6 Jul 2024 11:42:53 -0300 Subject: [PATCH 035/203] Rename symbol and module lookup struts --- crates/compiler/can/src/annotation.rs | 12 +++++-- crates/compiler/can/src/def.rs | 2 +- crates/compiler/can/src/env.rs | 12 +++---- crates/compiler/can/src/expr.rs | 10 ++++-- crates/compiler/can/src/scope.rs | 51 +++++++++++++++------------ 5 files changed, 53 insertions(+), 34 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 692188d1227..7fe445bec9b 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -1,6 +1,6 @@ use crate::env::Env; use crate::procedure::{QualifiedReference, References}; -use crate::scope::{LookedupSymbol, PendingAbilitiesInScope, Scope}; +use crate::scope::{PendingAbilitiesInScope, Scope, SymbolLookup}; use roc_collections::{ImMap, MutSet, SendMap, VecMap, VecSet}; use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::symbol::Symbol; @@ -386,7 +386,10 @@ pub(crate) fn make_apply_symbol( // Look it up in scope! match scope.lookup_str(ident, region) { - Ok(LookedupSymbol { symbol, params: _ }) => { + Ok(SymbolLookup { + symbol, + module_params: _, + }) => { references.insert_type_lookup(symbol, QualifiedReference::Unqualified); Ok(symbol) } @@ -398,7 +401,10 @@ pub(crate) fn make_apply_symbol( } } else { match env.qualified_lookup(scope, module_name, ident, region) { - Ok(LookedupSymbol { symbol, params: _ }) => { + Ok(SymbolLookup { + symbol, + module_params: _, + }) => { references.insert_type_lookup(symbol, QualifiedReference::Qualified); Ok(symbol) } diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index bca6db9fffd..9ee3bf692c0 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -507,7 +507,7 @@ fn canonicalize_alias<'a>( #[macro_export] macro_rules! params_in_abilities_unimplemented { ($lookup:expr) => { - match $lookup.params { + match $lookup.module_params { None => $lookup.symbol, Some(_) => unimplemented!("params in abilities"), } diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index b5062c5d972..1dfbe36b039 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -1,7 +1,7 @@ use std::path::Path; use crate::procedure::References; -use crate::scope::{LookedupModule, LookedupSymbol, Scope}; +use crate::scope::{ModuleLookup, Scope, SymbolLookup}; use bumpalo::Bump; use roc_collections::{MutMap, VecSet}; use roc_module::ident::{Ident, ModuleName}; @@ -78,7 +78,7 @@ impl<'a> Env<'a> { module_name_str: &str, ident: &str, region: Region, - ) -> Result { + ) -> Result { debug_assert!( !module_name_str.is_empty(), "Called env.qualified_lookup with an unqualified ident: {ident:?}" @@ -112,7 +112,7 @@ impl<'a> Env<'a> { module_id: ModuleId, ident: &str, region: Region, - ) -> Result { + ) -> Result { if let Some(module) = scope.modules.lookup_by_id(&module_id) { self.qualified_lookup_help(scope, module, ident, region) } else { @@ -124,10 +124,10 @@ impl<'a> Env<'a> { fn qualified_lookup_help( &mut self, scope: &Scope, - module: LookedupModule, + module: ModuleLookup, ident: &str, region: Region, - ) -> Result { + ) -> Result { let is_type_name = ident.starts_with(|c: char| c.is_uppercase()); // You can do qualified lookups on your own module, e.g. @@ -143,7 +143,7 @@ impl<'a> Env<'a> { self.qualified_value_lookups.insert(symbol); } - Ok(LookedupSymbol::no_params(symbol)) + Ok(SymbolLookup::no_params(symbol)) } None => { let error = RuntimeError::LookupNotInScope { diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 7a137947944..da000fe701f 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -10,7 +10,7 @@ use crate::num::{ use crate::params_in_abilities_unimplemented; use crate::pattern::{canonicalize_pattern, BindingsFromPattern, Pattern, PermitShadows}; use crate::procedure::{QualifiedReference, References}; -use crate::scope::{LookedupSymbol, Scope}; +use crate::scope::{Scope, SymbolLookup}; use crate::traverse::{walk_expr, Visitor}; use roc_collections::soa::Index; use roc_collections::{SendMap, VecMap, VecSet}; @@ -1934,7 +1934,13 @@ fn canonicalize_var_lookup( (can_expr, output) } -fn lookup_to_expr(LookedupSymbol { symbol, params }: LookedupSymbol, var: Variable) -> Expr { +fn lookup_to_expr( + SymbolLookup { + symbol, + module_params: params, + }: SymbolLookup, + var: Variable, +) -> Expr { if let Some(params) = params { Expr::ParamsVar { symbol, diff --git a/crates/compiler/can/src/scope.rs b/crates/compiler/can/src/scope.rs index 583561519db..6de854fa807 100644 --- a/crates/compiler/can/src/scope.rs +++ b/crates/compiler/can/src/scope.rs @@ -76,7 +76,7 @@ impl Scope { } } - pub fn lookup(&self, ident: &Ident, region: Region) -> Result { + pub fn lookup(&self, ident: &Ident, region: Region) -> Result { self.lookup_str(ident.as_str(), region) } @@ -91,7 +91,7 @@ impl Scope { .push(("Set".into(), Symbol::SET_SET, Region::zero())); } - pub fn lookup_str(&self, ident: &str, region: Region) -> Result { + pub fn lookup_str(&self, ident: &str, region: Region) -> Result { use ContainsIdent::*; match self.scope_contains_ident(ident) { @@ -205,14 +205,14 @@ impl Scope { } } - fn has_imported_symbol(&self, ident: &str) -> Option<(LookedupSymbol, Region)> { + fn has_imported_symbol(&self, ident: &str) -> Option<(SymbolLookup, Region)> { self.imported_symbols .iter() .find_map(|(import, symbol, original_region)| { if ident == import.as_str() { match self.modules.lookup_by_id(&symbol.module_id()) { Some(module) => Some((module.into_symbol(*symbol), *original_region)), - None => Some((LookedupSymbol::no_params(*symbol), *original_region)), + None => Some((SymbolLookup::no_params(*symbol), *original_region)), } } else { None @@ -394,9 +394,13 @@ impl Scope { region: Region, ) -> Result<(), (Symbol, Region)> { match self.scope_contains_ident(ident.as_str()) { - ContainsIdent::InScope(LookedupSymbol { symbol, params: _ }, region) => { - Err((symbol, region)) - } + ContainsIdent::InScope( + SymbolLookup { + symbol, + module_params: _, + }, + region, + ) => Err((symbol, region)), ContainsIdent::NotPresent | ContainsIdent::NotInScope(_) => { self.imported_symbols.push((ident, symbol, region)); Ok(()) @@ -541,7 +545,7 @@ pub fn create_alias( #[derive(Debug)] enum ContainsIdent { - InScope(LookedupSymbol, Region), + InScope(SymbolLookup, Region), NotInScope(IdentId), NotPresent, } @@ -582,7 +586,7 @@ impl ScopedIdentIds { let index = ident_id.index(); if self.in_scope[index] { return InScope( - LookedupSymbol::no_params(Symbol::new(self.home, ident_id)), + SymbolLookup::no_params(Symbol::new(self.home, ident_id)), self.regions[index], ); } else { @@ -699,21 +703,21 @@ impl ScopeModules { } } - pub fn lookup(&self, module_name: &ModuleName) -> Option { + pub fn lookup(&self, module_name: &ModuleName) -> Option { self.names .iter() .position(|name| name == module_name) - .map(|index| LookedupModule { + .map(|index| ModuleLookup { id: self.ids[index], params: self.params[index], }) } - pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option { + pub fn lookup_by_id(&self, module_id: &ModuleId) -> Option { self.ids .iter() .position(|id| id == module_id) - .map(|index| LookedupModule { + .map(|index| ModuleLookup { id: self.ids[index], params: self.params[index], }) @@ -765,14 +769,17 @@ impl ScopeModules { } #[derive(Debug, Clone)] -pub struct LookedupSymbol { +pub struct SymbolLookup { pub symbol: Symbol, - pub params: Option, + pub module_params: Option, } -impl LookedupSymbol { +impl SymbolLookup { pub fn new(symbol: Symbol, params: Option) -> Self { - Self { symbol, params } + Self { + symbol, + module_params: params, + } } pub fn no_params(symbol: Symbol) -> Self { @@ -780,18 +787,18 @@ impl LookedupSymbol { } } -pub struct LookedupModule { +pub struct ModuleLookup { pub id: ModuleId, pub params: Option, } -impl LookedupModule { - pub fn into_symbol(&self, symbol: Symbol) -> LookedupSymbol { +impl ModuleLookup { + pub fn into_symbol(&self, symbol: Symbol) -> SymbolLookup { debug_assert_eq!(symbol.module_id(), self.id); - LookedupSymbol { + SymbolLookup { symbol, - params: self.params, + module_params: self.params, } } } From 0cbb352a89a64dcc57b2c0afd9365de6571ed7a9 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 6 Jul 2024 21:36:26 -0300 Subject: [PATCH 036/203] Move unexpected params warning to solve --- crates/compiler/can/src/copy.rs | 9 +- crates/compiler/can/src/debug/pretty_print.rs | 6 +- crates/compiler/can/src/def.rs | 101 ++++++------------ crates/compiler/can/src/env.rs | 4 - crates/compiler/can/src/expr.rs | 27 +++-- crates/compiler/can/src/module.rs | 13 +-- crates/compiler/can/src/traverse.rs | 4 +- crates/compiler/can/tests/helpers/mod.rs | 2 - crates/compiler/constrain/src/expr.rs | 22 ++-- crates/compiler/load/tests/helpers/mod.rs | 1 - crates/compiler/load_internal/src/file.rs | 11 -- .../compiler/load_internal/tests/test_load.rs | 15 ++- crates/compiler/mono/src/ir.rs | 16 ++- crates/compiler/problem/src/can.rs | 8 +- crates/compiler/solve/src/solve.rs | 7 +- crates/compiler/solve_problem/src/lib.rs | 3 + crates/reporting/src/error/canonicalize.rs | 15 --- crates/reporting/src/error/type.rs | 20 ++++ 18 files changed, 115 insertions(+), 169 deletions(-) diff --git a/crates/compiler/can/src/copy.rs b/crates/compiler/can/src/copy.rs index dbf4fb79350..c8d3b632679 100644 --- a/crates/compiler/can/src/copy.rs +++ b/crates/compiler/can/src/copy.rs @@ -297,12 +297,13 @@ fn deep_copy_expr_help(env: &mut C, copied: &mut Vec, expr params: *params, var: sub!(*var), }, - ImportParams(loc_expr, var, module_id) => ImportParams( - Box::new(loc_expr.map(|e| go_help!(e))), - sub!(*var), + ImportParams(module_id, region, opt_provided) => ImportParams( *module_id, + *region, + opt_provided + .as_ref() + .map(|(var, expr)| (sub!(*var), Box::new(go_help!(&expr)))), ), - MissingImportParams(module_id, region) => MissingImportParams(*module_id, *region), &AbilityMember(sym, specialization, specialization_var) => { AbilityMember(sym, specialization, sub!(specialization_var)) } diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index 83edf1ca5f4..ad1f01f0ee4 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -209,9 +209,9 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, Var(sym, _) | ParamsVar { symbol: sym, .. } | AbilityMember(sym, _, _) => { pp_sym(c, f, *sym) } - ImportParams(loc_expr, _, _) => expr(c, p, f, &loc_expr.value), - MissingImportParams(module_id, _) => { - text!(f, "", module_id) + ImportParams(_, _, Some((_, params_expr))) => expr(c, p, f, params_expr), + ImportParams(module_id, _, None) => { + text!(f, "", module_id) } When { loc_cond, branches, .. diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 9ee3bf692c0..9d4bda6dacd 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -1153,21 +1153,12 @@ fn canonicalize_value_defs<'a>( exposed_symbols, }); - match params { - PendingModuleImportParams::None => {} - PendingModuleImportParams::Required { - symbol, - loc_pattern, - opt_provided, - } => { - pending_value_defs.push(PendingValueDef::ImportParams { - symbol, - loc_pattern, - module_id, - opt_provided, - }); - } - } + pending_value_defs.push(PendingValueDef::ImportParams { + symbol: params.symbol, + loc_pattern: params.loc_pattern, + opt_provided: params.opt_provided, + module_id, + }); } PendingValue::InvalidIngestedFile => { /* skip */ } PendingValue::ImportNameConflict => { /* skip */ } @@ -2421,7 +2412,7 @@ fn canonicalize_pending_value_def<'a>( .references .insert_value_lookup(symbol, QualifiedReference::Unqualified); - let (expr, references) = match opt_provided { + let (opt_var_record, references) = match opt_provided { Some(params) => { let (record, can_output) = canonicalize_record(env, var_store, scope, loc_pattern.region, params); @@ -2429,20 +2420,15 @@ fn canonicalize_pending_value_def<'a>( let references = can_output.references.clone(); output.union(can_output); - let loc_record = Loc::at(loc_pattern.region, record); - - let expr = - Expr::ImportParams(Box::new(loc_record), var_store.fresh(), module_id); - - (expr, references) + (Some((var_store.fresh(), Box::new(record))), references) } - None => ( - Expr::MissingImportParams(module_id, loc_pattern.region), - References::new(), - ), + None => (None, References::new()), }; - let loc_expr = Loc::at(loc_pattern.region, expr); + let loc_expr = Loc::at( + loc_pattern.region, + Expr::ImportParams(module_id, loc_pattern.region, opt_var_record), + ); let def = single_can_def( loc_pattern, @@ -3017,15 +3003,10 @@ struct PendingModuleImport<'a> { params: PendingModuleImportParams<'a>, } -enum PendingModuleImportParams<'a> { - /// The module does not require any params - None, - /// The module requires params, they may or may not have been provided - Required { - symbol: Symbol, - loc_pattern: Loc, - opt_provided: Option>>>>, - }, +struct PendingModuleImportParams<'a> { + symbol: Symbol, + loc_pattern: Loc, + opt_provided: Option>>>>, } pub struct IntroducedImport { @@ -3178,45 +3159,27 @@ fn to_pending_value_def<'a>( None => module_name.clone(), }; - let params = if env.modules_expecting_params.contains(&module_id) { - let name_str = name_with_alias.as_str(); - let sym_region = module_import.params.map(|p| p.params.region).unwrap_or(region); - - match scope.introduce_str(format!("#{name_str}").as_str(), sym_region) { - Ok(symbol) => { - let loc_pattern = Loc::at(sym_region, Pattern::Identifier(symbol)); - - PendingModuleImportParams::Required { - symbol, - loc_pattern, - opt_provided: module_import.params.map(|p| p.params.value), - } - }, - Err(_) => { - // Ignore conflict, it will be handled as a duplicate import - PendingModuleImportParams::None - }, - } + // Generate a symbol for the module params def + // We do this even if params weren't provided so that solve can report if they are missing + let params_sym = scope.gen_unique_symbol(); + let params_region = module_import.params.map(|p| p.params.region).unwrap_or(region); + let params = + PendingModuleImportParams { + symbol: params_sym, + loc_pattern: Loc::at(params_region, Pattern::Identifier(params_sym)), + opt_provided: module_import.params.map(|p| p.params.value), + }; + let provided_params_sym = if module_import.params.is_some() { + // Only add params to scope if they are provided + Some(params_sym) } else { - if let Some(params) = module_import.params { - env.problems.push(Problem::UnexpectedParams { - module_id, - region: params.params.region, - }); - } - - PendingModuleImportParams::None - }; - - let params_sym = match params { - PendingModuleImportParams::Required { symbol, .. } => Some(symbol), - PendingModuleImportParams::None => None, + None }; if let Err(existing_import) = scope .modules - .insert(name_with_alias.clone(), module_id, params_sym, region) + .insert(name_with_alias.clone(), module_id, provided_params_sym, region) { env.problems.push(Problem::ImportNameConflict { name: name_with_alias, diff --git a/crates/compiler/can/src/env.rs b/crates/compiler/can/src/env.rs index 1dfbe36b039..a5f72fd9064 100644 --- a/crates/compiler/can/src/env.rs +++ b/crates/compiler/can/src/env.rs @@ -21,8 +21,6 @@ pub struct Env<'a> { pub qualified_module_ids: &'a PackageModuleIds<'a>, - pub modules_expecting_params: VecSet, - /// Problems we've encountered along the way, which will be reported to the user at the end. pub problems: Vec, @@ -51,7 +49,6 @@ impl<'a> Env<'a> { home: ModuleId, module_path: &'a Path, dep_idents: &'a IdentIdsByModule, - modules_expecting_params: VecSet, qualified_module_ids: &'a PackageModuleIds<'a>, opt_shorthand: Option<&'a str>, ) -> Env<'a> { @@ -60,7 +57,6 @@ impl<'a> Env<'a> { home, module_path, dep_idents, - modules_expecting_params, qualified_module_ids, problems: Vec::new(), closures: MutMap::default(), diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index da000fe701f..4bfe9dad95c 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -185,10 +185,7 @@ pub enum Expr { }, /// Module params expression in import - ImportParams(Box>, Variable, ModuleId), - /// The imported module requires params but none were provided - /// We delay this error until solve, so we can report the expected type - MissingImportParams(ModuleId, Region), + ImportParams(ModuleId, Region, Option<(Variable, Box)>), /// The "crash" keyword Crash { @@ -342,8 +339,8 @@ impl Expr { Self::RecordAccessor(data) => Category::Accessor(data.field.clone()), Self::TupleAccess { index, .. } => Category::TupleAccess(*index), Self::RecordUpdate { .. } => Category::Record, - Self::ImportParams(loc_expr, _, _) => loc_expr.value.category(), - Self::MissingImportParams(_, _) => Category::Unknown, + Self::ImportParams(_, _, Some((_, expr))) => expr.category(), + Self::ImportParams(_, _, None) => Category::Unknown, Self::Tag { name, arguments, .. } => Category::TagApply { @@ -1976,7 +1973,6 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { | other @ TypedHole { .. } | other @ ForeignCall { .. } | other @ OpaqueWrapFunction(_) - | other @ MissingImportParams(_, _) | other @ Crash { .. } => other, List { @@ -2235,10 +2231,13 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr { ); } - ImportParams(loc_expr, var, module_id) => { - let loc_expr = Loc::at(loc_expr.region, inline_calls(var_store, loc_expr.value)); - ImportParams(Box::new(loc_expr), var, module_id) - } + ImportParams(module_id, region, Some((var, expr))) => ImportParams( + module_id, + region, + Some((var, Box::new(inline_calls(var_store, *expr)))), + ), + + ImportParams(module_id, region, None) => ImportParams(module_id, region, None), RecordAccess { record_var, @@ -3252,8 +3251,8 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec { Expr::Tuple { elems, .. } => { stack.extend(elems.iter().map(|(_, elem)| &elem.value)); } - Expr::ImportParams(loc_expr, _, _) => { - stack.push(&loc_expr.value); + Expr::ImportParams(_, _, Some((_, expr))) => { + stack.push(expr); } Expr::Expect { loc_continuation, .. @@ -3281,7 +3280,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec { | Expr::EmptyRecord | Expr::TypedHole(_) | Expr::RuntimeError(_) - | Expr::MissingImportParams(_, _) + | Expr::ImportParams(_, _, None) | Expr::OpaqueWrapFunction(_) => {} } } diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 283523ced21..ddcbcf43613 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -286,7 +286,6 @@ pub fn canonicalize_module_defs<'a>( qualified_module_ids: &'a PackageModuleIds<'a>, exposed_ident_ids: IdentIds, dep_idents: &'a IdentIdsByModule, - modules_expecting_params: VecSet, aliases: MutMap, imported_abilities_state: PendingAbilitiesStore, initial_scope: MutMap, @@ -312,7 +311,6 @@ pub fn canonicalize_module_defs<'a>( home, arena.alloc(Path::new(module_path)), dep_idents, - modules_expecting_params, qualified_module_ids, opt_shorthand, ); @@ -1153,7 +1151,6 @@ fn fix_values_captured_in_closure_expr( | TypedHole { .. } | RuntimeError(_) | ZeroArgumentTag { .. } - | MissingImportParams(_, _) | RecordAccessor { .. } => {} List { loc_elems, .. } => { @@ -1260,14 +1257,12 @@ fn fix_values_captured_in_closure_expr( } } - ImportParams(loc_expr, _, _) => { - fix_values_captured_in_closure_expr( - &mut loc_expr.value, - no_capture_symbols, - closure_captures, - ); + ImportParams(_, _, Some((_, expr))) => { + fix_values_captured_in_closure_expr(expr, no_capture_symbols, closure_captures); } + ImportParams(_, _, None) => {} + Tuple { elems, .. } => { for (_var, expr) in elems.iter_mut() { fix_values_captured_in_closure_expr( diff --git a/crates/compiler/can/src/traverse.rs b/crates/compiler/can/src/traverse.rs index ecc96ed2f86..9293f457abf 100644 --- a/crates/compiler/can/src/traverse.rs +++ b/crates/compiler/can/src/traverse.rs @@ -318,7 +318,8 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { .iter() .for_each(|(var, elem)| visitor.visit_expr(&elem.value, elem.region, *var)), Expr::EmptyRecord => { /* terminal */ } - Expr::ImportParams(expr, _, _) => visitor.visit_expr(&expr.value, expr.region, var), + Expr::ImportParams(_, region, Some((_, expr))) => visitor.visit_expr(expr, *region, var), + Expr::ImportParams(_, _, None) => { /* terminal */ } Expr::RecordAccess { field_var, loc_expr, @@ -404,7 +405,6 @@ pub fn walk_expr(visitor: &mut V, expr: &Expr, var: Variable) { } Expr::TypedHole(_) => { /* terminal */ } Expr::RuntimeError(..) => { /* terminal */ } - Expr::MissingImportParams(..) => { /* terminal */ } } } diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index 1b81ababc5d..508d84ea841 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -80,13 +80,11 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut ); let dep_idents = IdentIds::exposed_builtins(0); - let modules_expecting_params = Default::default(); let mut env = Env::new( arena, home, Path::new("Test.roc"), &dep_idents, - modules_expecting_params, &qualified_module_ids, None, ); diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index e3622b3326b..2b7448925bc 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -580,26 +580,20 @@ pub fn constrain_expr( constraints.and_constraint([store_expected, lookup_constr]) } - ImportParams(params, var, module_id) => { + ImportParams(module_id, region, Some((var, params))) => { let index = constraints.push_variable(*var); let expected_params = constraints.push_expected_type(Expected::ForReason( Reason::ImportParams(*module_id), index, - params.region, + *region, )); - let expr_con = constrain_expr( - types, - constraints, - env, - params.region, - ¶ms.value, - expected_params, - ); - let params_con = constraints.import_params(Some(index), *module_id, params.region); + let expr_con = + constrain_expr(types, constraints, env, *region, params, expected_params); + let params_con = constraints.import_params(Some(index), *module_id, *region); let expr_and_params = constraints.and_constraint([expr_con, params_con]); constraints.exists([*var], expr_and_params) } - MissingImportParams(module_id, region) => { + ImportParams(module_id, region, None) => { constraints.import_params(None, *module_id, *region) } &AbilityMember(symbol, specialization_id, specialization_var) => { @@ -4116,7 +4110,8 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool { return true; } OpaqueRef { argument, .. } => expr = &argument.1.value, - ImportParams(loc_expr, _, _) => expr = &loc_expr.value, + ImportParams(_, _, Some((_, params))) => expr = params, + ImportParams(_, _, None) => return false, Str(_) | IngestedFile(..) | List { .. } @@ -4139,7 +4134,6 @@ fn is_generalizable_expr(mut expr: &Expr) -> bool { | ExpectFx { .. } | Dbg { .. } | TypedHole(_) - | MissingImportParams(_, _) | RuntimeError(..) | ZeroArgumentTag { .. } | Tag { .. } diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 7b8ab16d034..63679c98208 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -189,7 +189,6 @@ pub fn can_expr_with<'a>( home, Path::new("Test.roc"), &dep_idents, - Default::default(), &module_ids, None, ); diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index d0d743d6abf..a1e9134b45d 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -250,7 +250,6 @@ fn start_phase<'a>( .clone(); let mut aliases = MutMap::default(); - let mut modules_expecting_params = VecSet::default(); let mut abilities_store = PendingAbilitiesStore::default(); for imported in parsed.available_modules.keys() { @@ -294,10 +293,6 @@ fn start_phase<'a>( .union(import_store.closure_from_imported(exposed_symbols)); } } - - if state.module_cache.param_patterns.contains_key(imported) { - modules_expecting_params.insert(*imported); - } } let skip_constraint_gen = { @@ -309,7 +304,6 @@ fn start_phase<'a>( BuildTask::CanonicalizeAndConstrain { parsed, dep_idents, - modules_expecting_params, exposed_symbols, qualified_module_ids, aliases, @@ -889,7 +883,6 @@ enum BuildTask<'a> { CanonicalizeAndConstrain { parsed: ParsedModule<'a>, qualified_module_ids: PackageModuleIds<'a>, - modules_expecting_params: VecSet, dep_idents: IdentIdsByModule, exposed_symbols: VecSet, aliases: MutMap, @@ -5001,7 +4994,6 @@ fn canonicalize_and_constrain<'a>( arena: &'a Bump, qualified_module_ids: &'a PackageModuleIds<'a>, dep_idents: IdentIdsByModule, - modules_expecting_params: VecSet, exposed_symbols: VecSet, aliases: MutMap, imported_abilities_state: PendingAbilitiesStore, @@ -5044,7 +5036,6 @@ fn canonicalize_and_constrain<'a>( qualified_module_ids, exposed_ident_ids, &dep_idents, - modules_expecting_params, aliases, imported_abilities_state, initial_scope, @@ -6207,7 +6198,6 @@ fn run_task<'a>( parsed, qualified_module_ids, dep_idents, - modules_expecting_params, exposed_symbols, aliases, abilities_store, @@ -6218,7 +6208,6 @@ fn run_task<'a>( arena, &qualified_module_ids, dep_idents, - modules_expecting_params, exposed_symbols, aliases, abilities_store, diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 9be7f65bd9e..75b8732f3a9 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -315,6 +315,13 @@ fn expect_types(mut loaded_module: LoadedModule, mut expected_types: HashMap<&st match declarations.declarations[index] { Value | Function(_) | Recursive(_) | TailRecursive(_) => { + let body = declarations.expressions[index].clone(); + + if let roc_can::expr::Expr::ImportParams(_, _, None) = body.value { + // Skip import defs without params + continue; + } + let symbol = declarations.symbols[index].value; let expr_var = declarations.variables[index]; @@ -443,7 +450,13 @@ fn module_with_deps() { match declarations.declarations[index] { Value | Function(_) | Recursive(_) | TailRecursive(_) => { - def_count += 1; + let body = declarations.expressions[index].clone(); + + if let roc_can::expr::Expr::ImportParams(_, _, None) = body.value { + // Skip import defs without params + } else { + def_count += 1; + } } Destructure(_) => { def_count += 1; diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index cfef124f1a1..9d1970fc978 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -4425,15 +4425,12 @@ pub fn with_hole<'a>( ParamsVar { .. } => { unimplemented!("module params code generation") } - ImportParams(loc_expr, _, _) => with_hole( - env, - loc_expr.value, - variable, - procs, - layout_cache, - assigned, - hole, - ), + ImportParams(_, _, Some((_, value))) => { + with_hole(env, *value, variable, procs, layout_cache, assigned, hole) + } + ImportParams(_, _, None) => { + internal_error!("Missing module params should've been dropped by now"); + } AbilityMember(member, specialization_id, specialization_var) => { let specialization_symbol = late_resolve_ability_specialization( env, @@ -5883,7 +5880,6 @@ pub fn with_hole<'a>( } TypedHole(_) => runtime_error(env, "Hit a blank"), RuntimeError(e) => runtime_error(env, env.arena.alloc(e.runtime_message())), - MissingImportParams(_, _) => runtime_error(env, env.arena.alloc("Missing import params")), Crash { msg, ret_var: _ } => { let msg_sym = possible_reuse_symbol_or_specialize( env, diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index 0d3d1284508..4e5d3e8ae58 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -236,10 +236,6 @@ pub enum Problem { one_occurrence: Region, kind: AliasKind, }, - UnexpectedParams { - module_id: ModuleId, - region: Region, - }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -319,7 +315,6 @@ impl Problem { Problem::OverAppliedCrash { .. } => RuntimeError, Problem::DefsOnlyUsedInRecursion(_, _) => Warning, Problem::FileProblem { .. } => Fatal, - Problem::UnexpectedParams { .. } => Warning, } } @@ -476,8 +471,7 @@ impl Problem { | Problem::UnnecessaryOutputWildcard { region } | Problem::OverAppliedCrash { region } | Problem::UnappliedCrash { region } - | Problem::DefsOnlyUsedInRecursion(_, region) - | Problem::UnexpectedParams { region, .. } => Some(*region), + | Problem::DefsOnlyUsedInRecursion(_, region) => Some(*region), Problem::RuntimeError(RuntimeError::CircularDef(cycle_entries)) | Problem::BadRecursion(cycle_entries) => { cycle_entries.first().map(|entry| entry.expr_region) diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index deb3354babc..9f6732a7f54 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1472,11 +1472,12 @@ fn solve( state } - (None, Some(_)) | (None, None) => { - // Module does not expect params. - // If provided still, canonicalization will produce a warning. + (None, Some(_)) => { + problems.push(TypeError::UnexpectedImportParams(*region, *module_id)); + state } + (None, None) => state, } } }; diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index df6b8ea28ef..8807f61b3a0 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -36,6 +36,7 @@ pub enum TypeError { }, IngestedFileBadUtf8(Box, Utf8Error), IngestedFileUnsupportedType(Box, ErrorType), + UnexpectedImportParams(Region, ModuleId), MissingImportParams(Region, ModuleId, ErrorType), ImportParamsMismatch(Region, ModuleId, ErrorType, ErrorType), } @@ -57,6 +58,7 @@ impl TypeError { TypeError::Exhaustive(exhtv) => exhtv.severity(), TypeError::StructuralSpecialization { .. } => RuntimeError, TypeError::WrongSpecialization { .. } => RuntimeError, + TypeError::UnexpectedImportParams(..) => Warning, TypeError::MissingImportParams(..) => RuntimeError, TypeError::ImportParamsMismatch(..) => RuntimeError, TypeError::IngestedFileBadUtf8(..) => Fatal, @@ -74,6 +76,7 @@ impl TypeError { | TypeError::StructuralSpecialization { region, .. } | TypeError::WrongSpecialization { region, .. } | TypeError::BadPatternMissingAbility(region, ..) + | TypeError::UnexpectedImportParams(region, ..) | TypeError::MissingImportParams(region, ..) | TypeError::ImportParamsMismatch(region, ..) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index ce41b1fb3bf..44a088a610a 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -64,7 +64,6 @@ const ABILITY_IMPLEMENTATION_NOT_IDENTIFIER: &str = "ABILITY IMPLEMENTATION NOT const DUPLICATE_IMPLEMENTATION: &str = "DUPLICATE IMPLEMENTATION"; const UNNECESSARY_IMPLEMENTATIONS: &str = "UNNECESSARY IMPLEMENTATIONS"; const INCOMPLETE_ABILITY_IMPLEMENTATION: &str = "INCOMPLETE ABILITY IMPLEMENTATION"; -const UNEXPECTED_PARAMS: &str = "UNEXPECTED PARAMS"; pub fn can_problem<'b>( alloc: &'b RocDocAllocator<'b>, @@ -1313,20 +1312,6 @@ pub fn can_problem<'b>( doc = report.doc; title = report.title; } - Problem::UnexpectedParams { module_id, region } => { - doc = alloc.stack([ - alloc.reflow("This import specifies module params:"), - alloc.region(lines.convert_region(region), severity), - alloc.concat([ - alloc.reflow("However, "), - alloc.module(module_id), - alloc.reflow( - " does not expect any. Did you intend to import a different module?", - ), - ]), - ]); - title = UNEXPECTED_PARAMS.to_string(); - } }; Report { diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index 84383c5a7e5..fd40955da2b 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -247,6 +247,26 @@ pub fn type_problem<'b>( severity, }) } + UnexpectedImportParams(region, module_id) => { + let stack = [ + alloc.reflow("This import specifies module params:"), + alloc.region(lines.convert_region(region), severity), + alloc.concat([ + alloc.reflow("However, "), + alloc.module(module_id), + alloc.reflow( + " does not expect any. Did you intend to import a different module?", + ), + ]), + ]; + + Some(Report { + title: "UNEXPECTED PARAMS".to_string(), + filename, + doc: alloc.stack(stack), + severity, + }) + } MissingImportParams(region, module_id, expected) => { let stack = [ alloc.reflow("This import specifies no module params:"), From 26fe91b02fd746e35795a052ea8bc7f3c00e67f6 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 6 Jul 2024 22:07:29 -0300 Subject: [PATCH 037/203] Always use "MODULE PARAMS" term in errors The theory is that this will be more searchable --- crates/compiler/load/tests/test_reporting.rs | 21 ------------------- .../compiler/load_internal/tests/test_load.rs | 10 ++++----- crates/compiler/solve/src/solve.rs | 6 +++--- crates/compiler/solve_problem/src/lib.rs | 18 ++++++++-------- crates/reporting/src/error/type.rs | 12 +++++------ 5 files changed, 23 insertions(+), 44 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index c4b99e2447b..781f48eba45 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -5005,27 +5005,6 @@ mod test_reporting { "### ); - test_report!( - unexpected_module_params, - indoc!( - r#" - import Dict { key: "abc" } exposing [empty] - - empty {} - "# - ),@r###" - ── UNEXPECTED PARAMS in /code/proj/Main.roc ──────────────────────────────────── - - This import specifies module params: - - 4│ import Dict { key: "abc" } exposing [empty] - ^^^^^^^^^^^^^^ - - However, Dict does not expect any. Did you intend to import a - different module? - "### - ); - test_report!( unfinished_import_as_or_exposing, indoc!( diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index 75b8732f3a9..8134c0e4390 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -1706,7 +1706,7 @@ fn module_params_typecheck_fail() { result, indoc!( r#" - ── IMPORT PARAMS MISMATCH in tmp/module_params_typecheck_fail/Main.roc ───────── + ── MODULE PARAMS MISMATCH in tmp/module_params_typecheck_fail/Main.roc ───────── Something is off with the params provided by this import: @@ -1757,7 +1757,7 @@ fn module_params_missing_fields() { result, indoc!( r#" - ── IMPORT PARAMS MISMATCH in tmp/module_params_missing_fields/Main.roc ───────── + ── MODULE PARAMS MISMATCH in tmp/module_params_missing_fields/Main.roc ───────── Something is off with the params provided by this import: @@ -1810,7 +1810,7 @@ fn module_params_extra_fields() { result, indoc!( r#" - ── IMPORT PARAMS MISMATCH in tmp/module_params_extra_fields/Main.roc ─────────── + ── MODULE PARAMS MISMATCH in tmp/module_params_extra_fields/Main.roc ─────────── Something is off with the params provided by this import: @@ -1863,7 +1863,7 @@ fn module_params_unexpected() { err, indoc!( r#" - ── UNEXPECTED PARAMS in tmp/module_params_unexpected/Main.roc ────────────────── + ── UNEXPECTED MODULE PARAMS in tmp/module_params_unexpected/Main.roc ─────────── This import specifies module params: @@ -1909,7 +1909,7 @@ fn module_params_missing() { err, indoc!( r#" - ── MISSING PARAMS in tmp/module_params_missing/Main.roc ──────────────────────── + ── MISSING MODULE PARAMS in tmp/module_params_missing/Main.roc ───────────────── This import specifies no module params: diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 9f6732a7f54..17bfb4c6370 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1450,7 +1450,7 @@ fn solve( Failure(vars, actual_type, expected_type, _) => { env.introduce(rank, &vars); - problems.push(TypeError::ImportParamsMismatch( + problems.push(TypeError::ModuleParamsMismatch( *region, *module_id, actual_type, @@ -1464,7 +1464,7 @@ fn solve( (Some(expected), None) => { let expected_type = env.uenv().var_to_error_type(*expected, Polarity::Neg); - problems.push(TypeError::MissingImportParams( + problems.push(TypeError::MissingModuleParams( *region, *module_id, expected_type, @@ -1473,7 +1473,7 @@ fn solve( state } (None, Some(_)) => { - problems.push(TypeError::UnexpectedImportParams(*region, *module_id)); + problems.push(TypeError::UnexpectedModuleParams(*region, *module_id)); state } diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 8807f61b3a0..9eabe601ba1 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -36,9 +36,9 @@ pub enum TypeError { }, IngestedFileBadUtf8(Box, Utf8Error), IngestedFileUnsupportedType(Box, ErrorType), - UnexpectedImportParams(Region, ModuleId), - MissingImportParams(Region, ModuleId, ErrorType), - ImportParamsMismatch(Region, ModuleId, ErrorType, ErrorType), + UnexpectedModuleParams(Region, ModuleId), + MissingModuleParams(Region, ModuleId, ErrorType), + ModuleParamsMismatch(Region, ModuleId, ErrorType, ErrorType), } impl TypeError { @@ -58,9 +58,9 @@ impl TypeError { TypeError::Exhaustive(exhtv) => exhtv.severity(), TypeError::StructuralSpecialization { .. } => RuntimeError, TypeError::WrongSpecialization { .. } => RuntimeError, - TypeError::UnexpectedImportParams(..) => Warning, - TypeError::MissingImportParams(..) => RuntimeError, - TypeError::ImportParamsMismatch(..) => RuntimeError, + TypeError::UnexpectedModuleParams(..) => Warning, + TypeError::MissingModuleParams(..) => RuntimeError, + TypeError::ModuleParamsMismatch(..) => RuntimeError, TypeError::IngestedFileBadUtf8(..) => Fatal, TypeError::IngestedFileUnsupportedType(..) => Fatal, } @@ -76,9 +76,9 @@ impl TypeError { | TypeError::StructuralSpecialization { region, .. } | TypeError::WrongSpecialization { region, .. } | TypeError::BadPatternMissingAbility(region, ..) - | TypeError::UnexpectedImportParams(region, ..) - | TypeError::MissingImportParams(region, ..) - | TypeError::ImportParamsMismatch(region, ..) => Some(*region), + | TypeError::UnexpectedModuleParams(region, ..) + | TypeError::MissingModuleParams(region, ..) + | TypeError::ModuleParamsMismatch(region, ..) => Some(*region), TypeError::UnfulfilledAbility(ab, ..) => ab.region(), TypeError::Exhaustive(e) => Some(e.region()), TypeError::CircularDef(c) => c.first().map(|ce| ce.symbol_region), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index fd40955da2b..86bbf471f05 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -247,7 +247,7 @@ pub fn type_problem<'b>( severity, }) } - UnexpectedImportParams(region, module_id) => { + UnexpectedModuleParams(region, module_id) => { let stack = [ alloc.reflow("This import specifies module params:"), alloc.region(lines.convert_region(region), severity), @@ -261,13 +261,13 @@ pub fn type_problem<'b>( ]; Some(Report { - title: "UNEXPECTED PARAMS".to_string(), + title: "UNEXPECTED MODULE PARAMS".to_string(), filename, doc: alloc.stack(stack), severity, }) } - MissingImportParams(region, module_id, expected) => { + MissingModuleParams(region, module_id, expected) => { let stack = [ alloc.reflow("This import specifies no module params:"), alloc.region(lines.convert_region(region), severity), @@ -283,13 +283,13 @@ pub fn type_problem<'b>( .indent(4), ]; Some(Report { - title: "MISSING PARAMS".to_string(), + title: "MISSING MODULE PARAMS".to_string(), filename, doc: alloc.stack(stack), severity, }) } - ImportParamsMismatch(region, module_id, actual_type, expected_type) => { + ModuleParamsMismatch(region, module_id, actual_type, expected_type) => { let stack = [ alloc.reflow("Something is off with the params provided by this import:"), alloc.region(lines.convert_region(region), severity), @@ -308,7 +308,7 @@ pub fn type_problem<'b>( ), ]; Some(Report { - title: "IMPORT PARAMS MISMATCH".to_string(), + title: "MODULE PARAMS MISMATCH".to_string(), filename, doc: alloc.stack(stack), severity, From 38413767840eea84207c0ba79d545809e08f3ad0 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Sat, 6 Jul 2024 22:52:38 -0300 Subject: [PATCH 038/203] update mono tests after adding import defs --- ...e_in_polymorphic_expression_issue_4717.txt | 128 +- .../test_mono/generated/binary_tree_fbip.txt | 4 +- .../generated/call_function_in_empty_list.txt | 4 +- .../call_function_in_empty_list_unbound.txt | 4 +- .../generated/capture_void_layout_task.txt | 52 +- ...ose_correct_recursion_var_under_record.txt | 88 +- .../generated/choose_i128_layout.txt | 4 +- .../generated/choose_u128_layout.txt | 4 +- .../test_mono/generated/choose_u64_layout.txt | 4 +- .../test_mono/generated/closure_in_list.txt | 4 +- ...lambda_set_productive_nullable_wrapped.txt | 54 +- .../test_mono/generated/dbg_in_expect.txt | 64 +- .../generated/dbg_str_followed_by_number.txt | 64 +- crates/compiler/test_mono/generated/dict.txt | 52 +- .../generated/empty_list_of_function_type.txt | 36 +- .../compiler/test_mono/generated/encode.txt | 18 +- .../encode_derived_nested_record_string.txt | 418 ++-- ...encode_derived_record_one_field_string.txt | 294 +-- ...ncode_derived_record_two_field_strings.txt | 300 +-- .../generated/encode_derived_string.txt | 166 +- .../encode_derived_tag_one_field_string.txt | 322 +-- ...encode_derived_tag_two_payloads_string.txt | 326 +-- .../test_mono/generated/factorial.txt | 8 +- ...zation_information_in_lambda_set_thunk.txt | 4 +- ...n_in_lambda_set_thunk_independent_defs.txt | 4 +- ...e_return_joinpoints_in_bool_lambda_set.txt | 4 +- ...e_return_joinpoints_in_enum_lambda_set.txt | 4 +- ..._return_joinpoints_in_union_lambda_set.txt | 4 +- .../generated/inspect_derived_dict.txt | 1960 ++++++++--------- .../generated/inspect_derived_list.txt | 234 +- .../inspect_derived_nested_record_string.txt | 390 ++-- .../generated/inspect_derived_record.txt | 276 +-- ...nspect_derived_record_one_field_string.txt | 238 +- ...spect_derived_record_two_field_strings.txt | 240 +- .../generated/inspect_derived_string.txt | 64 +- .../inspect_derived_tag_one_field_string.txt | 246 +-- ...nspect_derived_tag_two_payloads_string.txt | 252 +-- .../test_mono/generated/ir_int_add.txt | 8 +- .../compiler/test_mono/generated/ir_plus.txt | 4 +- .../compiler/test_mono/generated/ir_round.txt | 4 +- .../test_mono/generated/ir_two_defs.txt | 4 +- .../test_mono/generated/ir_when_idiv.txt | 28 +- .../test_mono/generated/ir_when_just.txt | 4 +- ...cialize_errors_behind_unified_branches.txt | 98 +- .../test_mono/generated/issue_4770.txt | 120 +- ..._4772_weakened_monomorphic_destructure.txt | 58 +- .../test_mono/generated/issue_6196.txt | 4 +- .../lambda_capture_niche_u8_vs_u64.txt | 8 +- ...pture_niches_with_other_lambda_capture.txt | 4 +- ...set_with_imported_toplevels_issue_4733.txt | 8 +- ...ure_with_multiple_recursive_structures.txt | 52 +- .../generated/linked_list_filter.txt | 12 +- .../test_mono/generated/linked_list_map.txt | 4 +- .../test_mono/generated/list_append.txt | 18 +- .../generated/list_append_closure.txt | 18 +- .../generated/list_cannot_update_inplace.txt | 42 +- .../compiler/test_mono/generated/list_get.txt | 36 +- .../compiler/test_mono/generated/list_len.txt | 12 +- .../generated/list_map_closure_borrows.txt | 50 +- .../generated/list_map_closure_owns.txt | 46 +- ...ist_map_take_capturing_or_noncapturing.txt | 24 +- .../generated/list_pass_to_function.txt | 36 +- .../test_mono/generated/list_sort_asc.txt | 16 +- .../generated/multiline_record_pattern.txt | 4 +- .../nested_optional_field_with_binary_op.txt | 4 +- .../generated/nested_pattern_match.txt | 4 +- .../num_width_gt_u8_layout_as_float.txt | 4 +- .../optional_field_with_binary_op.txt | 4 +- .../test_mono/generated/optional_when.txt | 4 +- .../polymorphic_expression_unification.txt | 4 +- .../test_mono/generated/quicksort_help.txt | 12 +- .../test_mono/generated/quicksort_swap.txt | 64 +- .../test_mono/generated/rb_tree_fbip.txt | 8 +- ...optional_field_function_no_use_default.txt | 4 +- ...rd_optional_field_function_use_default.txt | 4 +- ...cord_optional_field_let_no_use_default.txt | 4 +- .../record_optional_field_let_use_default.txt | 4 +- .../test_mono/generated/record_update.txt | 36 +- .../recursive_call_capturing_function.txt | 4 +- ...function_and_union_with_inference_hole.txt | 4 +- ..._set_resolved_only_upon_specialization.txt | 8 +- .../generated/recursively_build_effect.txt | 8 +- .../compiler/test_mono/generated/rigids.txt | 64 +- .../generated/specialize_after_match.txt | 8 +- .../generated/specialize_closures.txt | 8 +- .../generated/specialize_lowlevel.txt | 8 +- .../generated/tail_call_elimination.txt | 8 +- ...not_duplicate_identical_concrete_types.txt | 136 +- ...types_without_unification_of_unifiable.txt | 224 +- .../weakening_avoids_overspecialization.txt | 128 +- ...s_in_compiled_decision_tree_issue_5176.txt | 4 +- .../test_mono/generated/when_nested_maybe.txt | 4 +- .../test_mono/generated/when_on_record.txt | 4 +- .../generated/when_on_two_values.txt | 4 +- .../specialize/inspect/opaque_automatic.txt | 70 +- .../inspect/opaque_automatic_late.txt | 62 +- .../ability/specialize/set_eq_issue_4761.txt | 4 +- .../record_update_between_modules.txt | 12 +- 98 files changed, 3996 insertions(+), 3996 deletions(-) diff --git a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt index 21c653f0606..00585d6d584 100644 --- a/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt +++ b/crates/compiler/test_mono/generated/anonymous_closure_in_polymorphic_expression_issue_4717.txt @@ -2,92 +2,92 @@ procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure List.104 (List.488, List.489, List.490): - let List.592 : U64 = 0i64; - let List.593 : U64 = CallByName List.6 List.488; - let List.591 : [C U64, C U64] = CallByName List.80 List.488 List.489 List.490 List.592 List.593; - ret List.591; +procedure List.107 (List.491, List.492, List.493): + let List.595 : U64 = 0i64; + let List.596 : U64 = CallByName List.6 List.491; + let List.594 : [C U64, C U64] = CallByName List.80 List.491 List.492 List.493 List.595 List.596; + ret List.594; -procedure List.26 (List.201, List.202, List.203): - let List.585 : [C U64, C U64] = CallByName List.104 List.201 List.202 List.203; - let List.588 : U8 = 1i64; - let List.589 : U8 = GetTagId List.585; - let List.590 : Int1 = lowlevel Eq List.588 List.589; - if List.590 then - let List.204 : U64 = UnionAtIndex (Id 1) (Index 0) List.585; - ret List.204; +procedure List.26 (List.204, List.205, List.206): + let List.588 : [C U64, C U64] = CallByName List.107 List.204 List.205 List.206; + let List.591 : U8 = 1i64; + let List.592 : U8 = GetTagId List.588; + let List.593 : Int1 = lowlevel Eq List.591 List.592; + if List.593 then + let List.207 : U64 = UnionAtIndex (Id 1) (Index 0) List.588; + ret List.207; else - let List.205 : U64 = UnionAtIndex (Id 0) (Index 0) List.585; - ret List.205; + let List.208 : U64 = UnionAtIndex (Id 0) (Index 0) List.588; + ret List.208; -procedure List.38 (List.344, List.345): - let List.584 : U64 = CallByName List.6 List.344; - let List.346 : U64 = CallByName Num.77 List.584 List.345; - let List.574 : List U8 = CallByName List.43 List.344 List.346; - ret List.574; +procedure List.38 (List.347, List.348): + let List.587 : U64 = CallByName List.6 List.347; + let List.349 : U64 = CallByName Num.77 List.587 List.348; + let List.577 : List U8 = CallByName List.43 List.347 List.349; + ret List.577; -procedure List.43 (List.342, List.343): - let List.582 : U64 = CallByName List.6 List.342; - let List.581 : U64 = CallByName Num.77 List.582 List.343; - let List.576 : {U64, U64} = Struct {List.343, List.581}; - let List.575 : List U8 = CallByName List.49 List.342 List.576; - ret List.575; +procedure List.43 (List.345, List.346): + let List.585 : U64 = CallByName List.6 List.345; + let List.584 : U64 = CallByName Num.77 List.585 List.346; + let List.579 : {U64, U64} = Struct {List.346, List.584}; + let List.578 : List U8 = CallByName List.49 List.345 List.579; + ret List.578; -procedure List.49 (List.420, List.421): - let List.578 : U64 = StructAtIndex 1 List.421; - let List.579 : U64 = StructAtIndex 0 List.421; - let List.577 : List U8 = CallByName List.72 List.420 List.578 List.579; - ret List.577; +procedure List.49 (List.423, List.424): + let List.581 : U64 = StructAtIndex 1 List.424; + let List.582 : U64 = StructAtIndex 0 List.424; + let List.580 : List U8 = CallByName List.72 List.423 List.581 List.582; + ret List.580; procedure List.6 (#Attr.2): - let List.583 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.583; + let List.586 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.586; procedure List.66 (#Attr.2, #Attr.3): - let List.606 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.606; + let List.609 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.609; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.580 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.580; + let List.583 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.583; procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.594 List.491 List.492 List.493 List.494 List.495: - let List.596 : Int1 = CallByName Num.22 List.494 List.495; - if List.596 then - let List.605 : U8 = CallByName List.66 List.491 List.494; - let List.597 : [C U64, C U64] = CallByName Test.4 List.492 List.605; - let List.602 : U8 = 1i64; - let List.603 : U8 = GetTagId List.597; - let List.604 : Int1 = lowlevel Eq List.602 List.603; - if List.604 then - let List.496 : U64 = UnionAtIndex (Id 1) (Index 0) List.597; - let List.600 : U64 = 1i64; - let List.599 : U64 = CallByName Num.51 List.494 List.600; - jump List.594 List.491 List.496 List.493 List.599 List.495; + joinpoint List.597 List.494 List.495 List.496 List.497 List.498: + let List.599 : Int1 = CallByName Num.22 List.497 List.498; + if List.599 then + let List.608 : U8 = CallByName List.66 List.494 List.497; + let List.600 : [C U64, C U64] = CallByName Test.4 List.495 List.608; + let List.605 : U8 = 1i64; + let List.606 : U8 = GetTagId List.600; + let List.607 : Int1 = lowlevel Eq List.605 List.606; + if List.607 then + let List.499 : U64 = UnionAtIndex (Id 1) (Index 0) List.600; + let List.603 : U64 = 1i64; + let List.602 : U64 = CallByName Num.51 List.497 List.603; + jump List.597 List.494 List.499 List.496 List.602 List.498; else - dec List.491; - let List.497 : U64 = UnionAtIndex (Id 0) (Index 0) List.597; - let List.601 : [C U64, C U64] = TagId(0) List.497; - ret List.601; + dec List.494; + let List.500 : U64 = UnionAtIndex (Id 0) (Index 0) List.600; + let List.604 : [C U64, C U64] = TagId(0) List.500; + ret List.604; else - dec List.491; - let List.595 : [C U64, C U64] = TagId(1) List.492; - ret List.595; + dec List.494; + let List.598 : [C U64, C U64] = TagId(1) List.495; + ret List.598; in - jump List.594 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.597 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.77 (#Attr.2, #Attr.3): - let Num.280 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; + ret Num.282; procedure Test.1 (Test.2): let Test.13 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/binary_tree_fbip.txt b/crates/compiler/test_mono/generated/binary_tree_fbip.txt index 537c559e580..28940afe84b 100644 --- a/crates/compiler/test_mono/generated/binary_tree_fbip.txt +++ b/crates/compiler/test_mono/generated/binary_tree_fbip.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.4 (Test.27): let Test.39 : [C [, C *self *self] *self, ] = TagId(0) ; diff --git a/crates/compiler/test_mono/generated/call_function_in_empty_list.txt b/crates/compiler/test_mono/generated/call_function_in_empty_list.txt index 37e85f0111d..e16e90e7782 100644 --- a/crates/compiler/test_mono/generated/call_function_in_empty_list.txt +++ b/crates/compiler/test_mono/generated/call_function_in_empty_list.txt @@ -1,7 +1,7 @@ procedure List.5 (#Attr.2, #Attr.3): - let List.574 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; + let List.577 : List {} = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; decref #Attr.2; - ret List.574; + ret List.577; procedure Test.2 (Test.3): let Test.7 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt b/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt index 3613721f332..2386e3a6be2 100644 --- a/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt +++ b/crates/compiler/test_mono/generated/call_function_in_empty_list_unbound.txt @@ -1,7 +1,7 @@ procedure List.5 (#Attr.2, #Attr.3): - let List.574 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; + let List.577 : List [] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; decref #Attr.2; - ret List.574; + ret List.577; procedure Test.2 (Test.3): let Test.7 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/capture_void_layout_task.txt b/crates/compiler/test_mono/generated/capture_void_layout_task.txt index eaf6539940f..cc836855d18 100644 --- a/crates/compiler/test_mono/generated/capture_void_layout_task.txt +++ b/crates/compiler/test_mono/generated/capture_void_layout_task.txt @@ -1,39 +1,39 @@ -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : [C {}, C *self {{}, []}] = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : [C {}, C *self {{}, []}] = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : [] = CallByName List.66 List.163 List.166; - let List.168 : [C {}, C *self {{}, []}] = CallByName Test.29 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : [] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : [] = CallByName List.66 List.166 List.169; + let List.171 : [C {}, C *self {{}, []}] = CallByName Test.29 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15; + jump List.580 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Test.10 (Test.69, #Attr.12): let Test.72 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; diff --git a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt index ddb223ebd36..594ede0b57c 100644 --- a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt +++ b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt @@ -2,65 +2,65 @@ procedure Bool.1 (): let Bool.24 : Int1 = false; ret Bool.24; -procedure List.2 (List.108, List.109): - let List.588 : U64 = CallByName List.6 List.108; - let List.584 : Int1 = CallByName Num.22 List.109 List.588; - if List.584 then - let List.586 : Str = CallByName List.66 List.108 List.109; - inc List.586; - dec List.108; - let List.585 : [C {}, C Str] = TagId(1) List.586; - ret List.585; +procedure List.2 (List.111, List.112): + let List.591 : U64 = CallByName List.6 List.111; + let List.587 : Int1 = CallByName Num.22 List.112 List.591; + if List.587 then + let List.589 : Str = CallByName List.66 List.111 List.112; + inc List.589; + dec List.111; + let List.588 : [C {}, C Str] = TagId(1) List.589; + ret List.588; else - dec List.108; - let List.583 : {} = Struct {}; - let List.582 : [C {}, C Str] = TagId(0) List.583; - ret List.582; + dec List.111; + let List.586 : {} = Struct {}; + let List.585 : [C {}, C Str] = TagId(0) List.586; + ret List.585; procedure List.5 (#Attr.2, #Attr.3): - let List.590 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.10 #Attr.3; + let List.593 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.10 #Attr.3; decref #Attr.2; - ret List.590; + ret List.593; procedure List.6 (#Attr.2): - let List.589 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.589; + let List.592 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.592; procedure List.66 (#Attr.2, #Attr.3): - let List.587 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.587; + let List.590 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.590; -procedure List.9 (List.335): - let List.581 : U64 = 0i64; - let List.574 : [C {}, C Str] = CallByName List.2 List.335 List.581; - let List.578 : U8 = 1i64; - let List.579 : U8 = GetTagId List.574; - let List.580 : Int1 = lowlevel Eq List.578 List.579; - if List.580 then - let List.336 : Str = UnionAtIndex (Id 1) (Index 0) List.574; - let List.575 : [C {}, C Str] = TagId(1) List.336; - ret List.575; +procedure List.9 (List.338): + let List.584 : U64 = 0i64; + let List.577 : [C {}, C Str] = CallByName List.2 List.338 List.584; + let List.581 : U8 = 1i64; + let List.582 : U8 = GetTagId List.577; + let List.583 : Int1 = lowlevel Eq List.581 List.582; + if List.583 then + let List.339 : Str = UnionAtIndex (Id 1) (Index 0) List.577; + let List.578 : [C {}, C Str] = TagId(1) List.339; + ret List.578; else - dec List.574; - let List.577 : {} = Struct {}; - let List.576 : [C {}, C Str] = TagId(0) List.577; - ret List.576; + dec List.577; + let List.580 : {} = Struct {}; + let List.579 : [C {}, C Str] = TagId(0) List.580; + ret List.579; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; -procedure Result.5 (Result.10, Result.11): - let Result.37 : U8 = 1i64; - let Result.38 : U8 = GetTagId Result.10; - let Result.39 : Int1 = lowlevel Eq Result.37 Result.38; - if Result.39 then +procedure Result.5 (Result.11, Result.12): + let Result.38 : U8 = 1i64; + let Result.39 : U8 = GetTagId Result.11; + let Result.40 : Int1 = lowlevel Eq Result.38 Result.39; + if Result.40 then + dec Result.12; + let Result.13 : Str = UnionAtIndex (Id 1) (Index 0) Result.11; + ret Result.13; + else dec Result.11; - let Result.12 : Str = UnionAtIndex (Id 1) (Index 0) Result.10; ret Result.12; - else - dec Result.10; - ret Result.11; procedure Test.10 (Test.11): let Test.12 : Str = CallByName Test.2 Test.11; diff --git a/crates/compiler/test_mono/generated/choose_i128_layout.txt b/crates/compiler/test_mono/generated/choose_i128_layout.txt index e6047f606b9..1fb244b30f1 100644 --- a/crates/compiler/test_mono/generated/choose_i128_layout.txt +++ b/crates/compiler/test_mono/generated/choose_i128_layout.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : I128 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I128 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Test.0 (): let Test.6 : I128 = 18446744073709551616i64; diff --git a/crates/compiler/test_mono/generated/choose_u128_layout.txt b/crates/compiler/test_mono/generated/choose_u128_layout.txt index 4a8c66ef99b..d9d8b8b8422 100644 --- a/crates/compiler/test_mono/generated/choose_u128_layout.txt +++ b/crates/compiler/test_mono/generated/choose_u128_layout.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U128 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U128 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.2 : U128 = 170141183460469231731687303715884105728u128; diff --git a/crates/compiler/test_mono/generated/choose_u64_layout.txt b/crates/compiler/test_mono/generated/choose_u64_layout.txt index d532d662320..2c0493ca016 100644 --- a/crates/compiler/test_mono/generated/choose_u64_layout.txt +++ b/crates/compiler/test_mono/generated/choose_u64_layout.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.2 : U64 = 9999999999999999999i64; diff --git a/crates/compiler/test_mono/generated/closure_in_list.txt b/crates/compiler/test_mono/generated/closure_in_list.txt index b43f0cfd2f6..07693a0e389 100644 --- a/crates/compiler/test_mono/generated/closure_in_list.txt +++ b/crates/compiler/test_mono/generated/closure_in_list.txt @@ -1,6 +1,6 @@ procedure List.6 (#Attr.2): - let List.574 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.574; + let List.577 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.577; procedure Test.1 (Test.5): let Test.2 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt index a41cf0fd3a1..ea2468fa79a 100644 --- a/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt +++ b/crates/compiler/test_mono/generated/compose_recursive_lambda_set_productive_nullable_wrapped.txt @@ -2,46 +2,46 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : [, C *self Int1, C *self Int1] = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : [, C *self Int1, C *self Int1] = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : Int1 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; -procedure List.92 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : Int1 = CallByName List.66 List.163 List.166; - let List.168 : [, C *self Int1, C *self Int1] = CallByName Test.6 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; +procedure List.95 (#Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : Int1 = CallByName List.66 List.166 List.169; + let List.171 : [, C *self Int1, C *self Int1] = CallByName Test.6 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9; + jump List.580 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.234 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.234; + let Str.238 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.238; procedure Test.1 (Test.5): ret Test.5; diff --git a/crates/compiler/test_mono/generated/dbg_in_expect.txt b/crates/compiler/test_mono/generated/dbg_in_expect.txt index a1c97597ac0..92be41da95a 100644 --- a/crates/compiler/test_mono/generated/dbg_in_expect.txt +++ b/crates/compiler/test_mono/generated/dbg_in_expect.txt @@ -2,48 +2,48 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Inspect.246 (Inspect.247, Inspect.245): +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.323 : Str = "\""; + let Inspect.322 : Str = CallByName Inspect.63 Inspect.251 Inspect.323; + let Inspect.318 : Str = CallByName Inspect.63 Inspect.322 Inspect.249; let Inspect.319 : Str = "\""; - let Inspect.318 : Str = CallByName Inspect.59 Inspect.247 Inspect.319; - let Inspect.314 : Str = CallByName Inspect.59 Inspect.318 Inspect.245; - let Inspect.315 : Str = "\""; - let Inspect.313 : Str = CallByName Inspect.59 Inspect.314 Inspect.315; - ret Inspect.313; + let Inspect.317 : Str = CallByName Inspect.63 Inspect.318 Inspect.319; + ret Inspect.317; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; -procedure Inspect.43 (Inspect.245): - let Inspect.309 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.309; +procedure Inspect.47 (Inspect.249): + let Inspect.313 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.313; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : Str = CallByName Inspect.43 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName Inspect.246 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : Str = CallByName Inspect.47 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName Inspect.250 Inspect.308 Inspect.312; + ret Inspect.307; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.317 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.317; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.321 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.321; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.1 (): let Test.4 : Str = ""; diff --git a/crates/compiler/test_mono/generated/dbg_str_followed_by_number.txt b/crates/compiler/test_mono/generated/dbg_str_followed_by_number.txt index 0bd7f21f88b..27a5e689c12 100644 --- a/crates/compiler/test_mono/generated/dbg_str_followed_by_number.txt +++ b/crates/compiler/test_mono/generated/dbg_str_followed_by_number.txt @@ -1,45 +1,45 @@ -procedure Inspect.246 (Inspect.247, Inspect.245): +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.323 : Str = "\""; + let Inspect.322 : Str = CallByName Inspect.63 Inspect.251 Inspect.323; + let Inspect.318 : Str = CallByName Inspect.63 Inspect.322 Inspect.249; let Inspect.319 : Str = "\""; - let Inspect.318 : Str = CallByName Inspect.59 Inspect.247 Inspect.319; - let Inspect.314 : Str = CallByName Inspect.59 Inspect.318 Inspect.245; - let Inspect.315 : Str = "\""; - let Inspect.313 : Str = CallByName Inspect.59 Inspect.314 Inspect.315; - ret Inspect.313; + let Inspect.317 : Str = CallByName Inspect.63 Inspect.318 Inspect.319; + ret Inspect.317; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; -procedure Inspect.43 (Inspect.245): - let Inspect.309 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.309; +procedure Inspect.47 (Inspect.249): + let Inspect.313 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.313; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : Str = CallByName Inspect.43 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName Inspect.246 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : Str = CallByName Inspect.47 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName Inspect.250 Inspect.308 Inspect.312; + ret Inspect.307; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.317 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.317; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.321 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.321; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.3 : Str = ""; diff --git a/crates/compiler/test_mono/generated/dict.txt b/crates/compiler/test_mono/generated/dict.txt index 9b74ec2739a..09fa9a1157f 100644 --- a/crates/compiler/test_mono/generated/dict.txt +++ b/crates/compiler/test_mono/generated/dict.txt @@ -1,37 +1,37 @@ -procedure Dict.1 (Dict.723): - let Dict.732 : List {U32, U32} = Array []; - let Dict.733 : List {[], []} = Array []; - let Dict.734 : U64 = 0i64; - let Dict.44 : Float32 = CallByName Dict.44; - let Dict.45 : U8 = CallByName Dict.45; - let Dict.731 : {List {U32, U32}, List {[], []}, U64, Float32, U8} = Struct {Dict.732, Dict.733, Dict.734, Dict.44, Dict.45}; - ret Dict.731; +procedure Dict.1 (Dict.730): + let Dict.739 : List {U32, U32} = Array []; + let Dict.740 : List {[], []} = Array []; + let Dict.741 : U64 = 0i64; + let Dict.51 : Float32 = CallByName Dict.51; + let Dict.52 : U8 = CallByName Dict.52; + let Dict.738 : {List {U32, U32}, List {[], []}, U64, Float32, U8} = Struct {Dict.739, Dict.740, Dict.741, Dict.51, Dict.52}; + ret Dict.738; -procedure Dict.4 (Dict.729): - let Dict.156 : List {[], []} = StructAtIndex 1 Dict.729; - let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.729; +procedure Dict.4 (Dict.736): + let Dict.163 : List {[], []} = StructAtIndex 1 Dict.736; + let #Derived_gen.0 : List {U32, U32} = StructAtIndex 0 Dict.736; dec #Derived_gen.0; - let Dict.730 : U64 = CallByName List.6 Dict.156; - dec Dict.156; - ret Dict.730; + let Dict.737 : U64 = CallByName List.6 Dict.163; + dec Dict.163; + ret Dict.737; -procedure Dict.44 (): - let Dict.738 : Float32 = 0.8f64; - ret Dict.738; +procedure Dict.51 (): + let Dict.745 : Float32 = 0.8f64; + ret Dict.745; -procedure Dict.45 (): - let Dict.736 : U8 = 64i64; - let Dict.737 : U8 = 3i64; - let Dict.735 : U8 = CallByName Num.75 Dict.736 Dict.737; - ret Dict.735; +procedure Dict.52 (): + let Dict.743 : U8 = 64i64; + let Dict.744 : U8 = 3i64; + let Dict.742 : U8 = CallByName Num.75 Dict.743 Dict.744; + ret Dict.742; procedure List.6 (#Attr.2): - let List.574 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.574; + let List.577 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.577; procedure Num.75 (#Attr.2, #Attr.3): - let Num.279 : U8 = lowlevel NumSubWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U8 = lowlevel NumSubWrap #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.3 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt index 66b2f2df9f3..6f6692435b9 100644 --- a/crates/compiler/test_mono/generated/empty_list_of_function_type.txt +++ b/crates/compiler/test_mono/generated/empty_list_of_function_type.txt @@ -2,31 +2,31 @@ procedure Bool.1 (): let Bool.23 : Int1 = false; ret Bool.23; -procedure List.2 (List.108, List.109): - let List.580 : U64 = CallByName List.6 List.108; - let List.576 : Int1 = CallByName Num.22 List.109 List.580; - if List.576 then - let List.578 : {} = CallByName List.66 List.108 List.109; - dec List.108; - let List.577 : [C {}, C {}] = TagId(1) List.578; - ret List.577; +procedure List.2 (List.111, List.112): + let List.583 : U64 = CallByName List.6 List.111; + let List.579 : Int1 = CallByName Num.22 List.112 List.583; + if List.579 then + let List.581 : {} = CallByName List.66 List.111 List.112; + dec List.111; + let List.580 : [C {}, C {}] = TagId(1) List.581; + ret List.580; else - dec List.108; - let List.575 : {} = Struct {}; - let List.574 : [C {}, C {}] = TagId(0) List.575; - ret List.574; + dec List.111; + let List.578 : {} = Struct {}; + let List.577 : [C {}, C {}] = TagId(0) List.578; + ret List.577; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; procedure List.66 (#Attr.2, #Attr.3): - let List.579 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.579; + let List.582 : {} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Test.2 (Test.5): dec Test.5; diff --git a/crates/compiler/test_mono/generated/encode.txt b/crates/compiler/test_mono/generated/encode.txt index 3a709c4bb54..f4782280573 100644 --- a/crates/compiler/test_mono/generated/encode.txt +++ b/crates/compiler/test_mono/generated/encode.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.124, List.125): - let List.577 : U64 = 1i64; - let List.575 : List U8 = CallByName List.70 List.124 List.577; - let List.574 : List U8 = CallByName List.71 List.575 List.125; - ret List.574; +procedure List.4 (List.127, List.128): + let List.580 : U64 = 1i64; + let List.578 : List U8 = CallByName List.70 List.127 List.580; + let List.577 : List U8 = CallByName List.71 List.578 List.128; + ret List.577; procedure List.70 (#Attr.2, #Attr.3): - let List.578 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.578; + let List.581 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.581; procedure List.71 (#Attr.2, #Attr.3): - let List.576 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.576; + let List.579 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.579; procedure Test.23 (Test.24, Test.35, Test.22): let Test.37 : List U8 = CallByName List.4 Test.24 Test.22; diff --git a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt index 84ac36e1fb0..f43f29560be 100644 --- a/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_nested_record_string.txt @@ -7,7 +7,7 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.8 : Str = CallByName #Derived.5 #Derived.1; let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; - let #Derived_gen.4 : List {Str, Str} = CallByName Test.21 #Derived_gen.5; + let #Derived_gen.4 : List {Str, Str} = CallByName Test.22 #Derived_gen.5; let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; @@ -17,260 +17,260 @@ procedure #Derived.5 (#Derived.6): procedure #Derived.7 (#Derived.8, #Derived.9, #Derived.6): let #Derived_gen.17 : Str = "b"; - let #Derived_gen.18 : Str = CallByName Test.19 #Derived.6; + let #Derived_gen.18 : Str = CallByName Test.20 #Derived.6; let #Derived_gen.16 : {Str, Str} = Struct {#Derived_gen.17, #Derived_gen.18}; let #Derived_gen.15 : List {Str, Str} = Array [#Derived_gen.16]; - let #Derived_gen.14 : List {Str, Str} = CallByName Test.21 #Derived_gen.15; + let #Derived_gen.14 : List {Str, Str} = CallByName Test.22 #Derived_gen.15; let #Derived_gen.13 : List U8 = CallByName Encode.24 #Derived.8 #Derived_gen.14 #Derived.9; ret #Derived_gen.13; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; - ret Encode.111; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.113 : List U8 = CallByName Test.67 Encode.99 Encode.101 Encode.107; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName #Derived.2 Encode.101 Encode.103 Encode.109; ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.116 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; - ret Encode.116; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.119 : List U8 = CallByName Test.67 Encode.99 Encode.101 Encode.107; - ret Encode.119; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.124 : List U8 = CallByName Test.56 Encode.99 Encode.101 Encode.107; - ret Encode.124; - -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : Str = CallByName #Derived.0 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; - -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; - -procedure List.18 (List.160, List.161, List.162): - let List.601 : U64 = 0i64; - let List.602 : U64 = CallByName List.6 List.160; - let List.600 : List U8 = CallByName List.92 List.160 List.161 List.162 List.601 List.602; - ret List.600; - -procedure List.4 (List.124, List.125): - let List.622 : U64 = 1i64; - let List.621 : List U8 = CallByName List.70 List.124 List.622; - let List.620 : List U8 = CallByName List.71 List.621 List.125; - ret List.620; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.115 : List U8 = CallByName Test.68 Encode.101 Encode.103 Encode.109; + ret Encode.115; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.118 : List U8 = CallByName #Derived.7 Encode.101 Encode.103 Encode.109; + ret Encode.118; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.121 : List U8 = CallByName Test.68 Encode.101 Encode.103 Encode.109; + ret Encode.121; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.126 : List U8 = CallByName Test.57 Encode.101 Encode.103 Encode.109; + ret Encode.126; + +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : Str = CallByName #Derived.0 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; + +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; + +procedure List.18 (List.163, List.164, List.165): + let List.604 : U64 = 0i64; + let List.605 : U64 = CallByName List.6 List.163; + let List.603 : List U8 = CallByName List.95 List.163 List.164 List.165 List.604 List.605; + ret List.603; + +procedure List.4 (List.127, List.128): + let List.625 : U64 = 1i64; + let List.624 : List U8 = CallByName List.70 List.127 List.625; + let List.623 : List U8 = CallByName List.71 List.624 List.128; + ret List.623; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.6 (#Attr.2): - let List.625 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.625; + let List.628 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.628; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.66 (#Attr.2, #Attr.3): - let List.610 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.610; + let List.613 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.613; procedure List.70 (#Attr.2, #Attr.3): - let List.616 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.616; + let List.619 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.619; procedure List.71 (#Attr.2, #Attr.3): - let List.614 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.614; + let List.617 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.617; procedure List.8 (#Attr.2, #Attr.3): - let List.624 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.624; - -procedure List.92 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): - joinpoint List.603 List.163 List.164 List.165 List.166 List.167: - let List.605 : Int1 = CallByName Num.22 List.166 List.167; - if List.605 then - let List.609 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.609; - let List.168 : List U8 = CallByName Test.70 List.164 List.609; - let List.608 : U64 = 1i64; - let List.607 : U64 = CallByName Num.51 List.166 List.608; - jump List.603 List.163 List.168 List.165 List.607 List.167; + let List.627 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.627; + +procedure List.95 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.71 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.603 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; - -procedure List.92 (#Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.70 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + jump List.580 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; + +procedure List.95 (#Derived_gen.40, #Derived_gen.41, #Derived_gen.42, #Derived_gen.43, #Derived_gen.44): + joinpoint List.606 List.166 List.167 List.168 List.169 List.170: + let List.608 : Int1 = CallByName Num.22 List.169 List.170; + if List.608 then + let List.612 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.612; + let List.171 : List U8 = CallByName Test.71 List.167 List.612; + let List.611 : U64 = 1i64; + let List.610 : U64 = CallByName Num.51 List.169 List.611; + jump List.606 List.166 List.171 List.168 List.610 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41; + jump List.606 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44; procedure Num.127 (#Attr.2): - let Num.284 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.284; + let Num.286 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.286; procedure Num.22 (#Attr.2, #Attr.3): - let Num.286 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.286; + let Num.288 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.288; procedure Num.51 (#Attr.2, #Attr.3): - let Num.285 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.285; + let Num.287 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.287; procedure Num.96 (#Attr.2): - let Num.283 : Str = lowlevel NumToStr #Attr.2; - ret Num.283; + let Num.285 : Str = lowlevel NumToStr #Attr.2; + ret Num.285; procedure Str.12 (#Attr.2): - let Str.244 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.244; + let Str.248 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.248; procedure Str.36 (#Attr.2): - let Str.245 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.245; + let Str.249 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.249; procedure Str.43 (#Attr.2): - let Str.239 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; - ret Str.239; - -procedure Str.9 (Str.67): - let Str.68 : {U64, Str, Int1, U8} = CallByName Str.43 Str.67; - let Str.236 : Int1 = StructAtIndex 2 Str.68; - if Str.236 then - let Str.238 : Str = StructAtIndex 1 Str.68; - let Str.237 : [C {U64, U8}, C Str] = TagId(1) Str.238; - ret Str.237; + let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + ret Str.243; + +procedure Str.9 (Str.71): + let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71; + let Str.240 : Int1 = StructAtIndex 2 Str.72; + if Str.240 then + let Str.242 : Str = StructAtIndex 1 Str.72; + let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242; + ret Str.241; else - let Str.234 : U8 = StructAtIndex 3 Str.68; - let Str.235 : U64 = StructAtIndex 0 Str.68; - let #Derived_gen.45 : Str = StructAtIndex 1 Str.68; + let Str.238 : U8 = StructAtIndex 3 Str.72; + let Str.239 : U64 = StructAtIndex 0 Str.72; + let #Derived_gen.45 : Str = StructAtIndex 1 Str.72; dec #Derived_gen.45; - let Str.233 : {U64, U8} = Struct {Str.235, Str.234}; - let Str.232 : [C {U64, U8}, C Str] = TagId(0) Str.233; - ret Str.232; - -procedure Test.19 (Test.55): - let Test.324 : Str = CallByName Encode.23 Test.55; - ret Test.324; - -procedure Test.2 (): - let Test.257 : {} = Struct {}; - ret Test.257; - -procedure Test.21 (Test.66): - let Test.260 : List {Str, Str} = CallByName Encode.23 Test.66; - ret Test.260; - -procedure Test.21 (Test.66): - let Test.292 : List {Str, Str} = CallByName Encode.23 Test.66; - ret Test.292; - -procedure Test.3 (Test.48, Test.49, Test.50): - let Test.321 : U8 = CallByName Num.127 Test.49; - let Test.318 : List U8 = CallByName List.4 Test.48 Test.321; - let Test.320 : Str = CallByName Num.96 Test.50; - let Test.319 : List U8 = CallByName Str.12 Test.320; - let Test.316 : List U8 = CallByName List.8 Test.318 Test.319; - let Test.317 : U8 = 32i64; - let Test.315 : List U8 = CallByName List.4 Test.316 Test.317; - ret Test.315; - -procedure Test.56 (Test.57, Test.274, Test.55): - let Test.313 : I64 = 115i64; - let Test.314 : U64 = CallByName Str.36 Test.55; - let Test.311 : List U8 = CallByName Test.3 Test.57 Test.313 Test.314; - let Test.312 : List U8 = CallByName Str.12 Test.55; - let Test.309 : List U8 = CallByName List.8 Test.311 Test.312; - let Test.310 : U8 = 32i64; - let Test.308 : List U8 = CallByName List.4 Test.309 Test.310; - ret Test.308; - -procedure Test.67 (Test.68, Test.262, Test.66): - let Test.290 : I64 = 114i64; - let Test.291 : U64 = CallByName List.6 Test.66; - let Test.69 : List U8 = CallByName Test.3 Test.68 Test.290 Test.291; - let Test.265 : {} = Struct {}; - let Test.264 : List U8 = CallByName List.18 Test.66 Test.69 Test.265; - ret Test.264; - -procedure Test.67 (Test.68, Test.262, Test.66): - let Test.322 : I64 = 114i64; - let Test.323 : U64 = CallByName List.6 Test.66; - let Test.69 : List U8 = CallByName Test.3 Test.68 Test.322 Test.323; - let Test.297 : {} = Struct {}; - let Test.296 : List U8 = CallByName List.18 Test.66 Test.69 Test.297; - ret Test.296; - -procedure Test.70 (Test.71, Test.266): - let Test.72 : Str = StructAtIndex 0 Test.266; - let Test.73 : Str = StructAtIndex 1 Test.266; - let Test.270 : Str = CallByName Test.19 Test.72; - let Test.271 : {} = Struct {}; - let Test.268 : List U8 = CallByName Encode.24 Test.71 Test.270 Test.271; - let Test.269 : {} = Struct {}; - let Test.267 : List U8 = CallByName Encode.24 Test.268 Test.73 Test.269; - ret Test.267; - -procedure Test.70 (Test.71, Test.266): - let Test.72 : Str = StructAtIndex 0 Test.266; - let Test.73 : Str = StructAtIndex 1 Test.266; - let Test.302 : Str = CallByName Test.19 Test.72; - let Test.303 : {} = Struct {}; - let Test.300 : List U8 = CallByName Encode.24 Test.71 Test.302 Test.303; - let Test.301 : {} = Struct {}; - let Test.299 : List U8 = CallByName Encode.24 Test.300 Test.73 Test.301; - ret Test.299; + let Str.237 : {U64, U8} = Struct {Str.239, Str.238}; + let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237; + ret Str.236; + +procedure Test.20 (Test.56): + let Test.325 : Str = CallByName Encode.23 Test.56; + ret Test.325; + +procedure Test.22 (Test.67): + let Test.261 : List {Str, Str} = CallByName Encode.23 Test.67; + ret Test.261; + +procedure Test.22 (Test.67): + let Test.293 : List {Str, Str} = CallByName Encode.23 Test.67; + ret Test.293; + +procedure Test.3 (): + let Test.258 : {} = Struct {}; + ret Test.258; + +procedure Test.4 (Test.49, Test.50, Test.51): + let Test.322 : U8 = CallByName Num.127 Test.50; + let Test.319 : List U8 = CallByName List.4 Test.49 Test.322; + let Test.321 : Str = CallByName Num.96 Test.51; + let Test.320 : List U8 = CallByName Str.12 Test.321; + let Test.317 : List U8 = CallByName List.8 Test.319 Test.320; + let Test.318 : U8 = 32i64; + let Test.316 : List U8 = CallByName List.4 Test.317 Test.318; + ret Test.316; + +procedure Test.57 (Test.58, Test.275, Test.56): + let Test.314 : I64 = 115i64; + let Test.315 : U64 = CallByName Str.36 Test.56; + let Test.312 : List U8 = CallByName Test.4 Test.58 Test.314 Test.315; + let Test.313 : List U8 = CallByName Str.12 Test.56; + let Test.310 : List U8 = CallByName List.8 Test.312 Test.313; + let Test.311 : U8 = 32i64; + let Test.309 : List U8 = CallByName List.4 Test.310 Test.311; + ret Test.309; + +procedure Test.68 (Test.69, Test.263, Test.67): + let Test.291 : I64 = 114i64; + let Test.292 : U64 = CallByName List.6 Test.67; + let Test.70 : List U8 = CallByName Test.4 Test.69 Test.291 Test.292; + let Test.266 : {} = Struct {}; + let Test.265 : List U8 = CallByName List.18 Test.67 Test.70 Test.266; + ret Test.265; + +procedure Test.68 (Test.69, Test.263, Test.67): + let Test.323 : I64 = 114i64; + let Test.324 : U64 = CallByName List.6 Test.67; + let Test.70 : List U8 = CallByName Test.4 Test.69 Test.323 Test.324; + let Test.298 : {} = Struct {}; + let Test.297 : List U8 = CallByName List.18 Test.67 Test.70 Test.298; + ret Test.297; + +procedure Test.71 (Test.72, Test.267): + let Test.73 : Str = StructAtIndex 0 Test.267; + let Test.74 : Str = StructAtIndex 1 Test.267; + let Test.271 : Str = CallByName Test.20 Test.73; + let Test.272 : {} = Struct {}; + let Test.269 : List U8 = CallByName Encode.24 Test.72 Test.271 Test.272; + let Test.270 : {} = Struct {}; + let Test.268 : List U8 = CallByName Encode.24 Test.269 Test.74 Test.270; + ret Test.268; + +procedure Test.71 (Test.72, Test.267): + let Test.73 : Str = StructAtIndex 0 Test.267; + let Test.74 : Str = StructAtIndex 1 Test.267; + let Test.303 : Str = CallByName Test.20 Test.73; + let Test.304 : {} = Struct {}; + let Test.301 : List U8 = CallByName Encode.24 Test.72 Test.303 Test.304; + let Test.302 : {} = Struct {}; + let Test.300 : List U8 = CallByName Encode.24 Test.301 Test.74 Test.302; + ret Test.300; procedure Test.0 (): - let Test.259 : Str = "bar"; - let Test.256 : {} = CallByName Test.2; - let Test.254 : List U8 = CallByName Encode.26 Test.259 Test.256; - let Test.209 : [C {U64, U8}, C Str] = CallByName Str.9 Test.254; - let Test.251 : U8 = 1i64; - let Test.252 : U8 = GetTagId Test.209; - let Test.253 : Int1 = lowlevel Eq Test.251 Test.252; - if Test.253 then - let Test.210 : Str = UnionAtIndex (Id 1) (Index 0) Test.209; - ret Test.210; + let Test.260 : Str = "bar"; + let Test.257 : {} = CallByName Test.3; + let Test.255 : List U8 = CallByName Encode.26 Test.260 Test.257; + let Test.210 : [C {U64, U8}, C Str] = CallByName Str.9 Test.255; + let Test.252 : U8 = 1i64; + let Test.253 : U8 = GetTagId Test.210; + let Test.254 : Int1 = lowlevel Eq Test.252 Test.253; + if Test.254 then + let Test.211 : Str = UnionAtIndex (Id 1) (Index 0) Test.210; + ret Test.211; else - dec Test.209; - let Test.250 : Str = ""; - ret Test.250; + dec Test.210; + let Test.251 : Str = ""; + ret Test.251; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt index ce4b3d29179..aaf154b15e9 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_one_field_string.txt @@ -4,194 +4,194 @@ procedure #Derived.0 (#Derived.1): procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.7 : Str = "a"; - let #Derived_gen.8 : Str = CallByName Test.19 #Derived.1; + let #Derived_gen.8 : Str = CallByName Test.20 #Derived.1; let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; - let #Derived_gen.4 : List {Str, Str} = CallByName Test.21 #Derived_gen.5; + let #Derived_gen.4 : List {Str, Str} = CallByName Test.22 #Derived_gen.5; let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; - ret Encode.111; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.113 : List U8 = CallByName Test.67 Encode.99 Encode.101 Encode.107; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName #Derived.2 Encode.101 Encode.103 Encode.109; ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName Test.56 Encode.99 Encode.101 Encode.107; - ret Encode.118; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.115 : List U8 = CallByName Test.68 Encode.101 Encode.103 Encode.109; + ret Encode.115; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.120 : List U8 = CallByName Test.57 Encode.101 Encode.103 Encode.109; + ret Encode.120; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : Str = CallByName #Derived.0 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : Str = CallByName #Derived.0 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; -procedure List.4 (List.124, List.125): - let List.596 : U64 = 1i64; - let List.595 : List U8 = CallByName List.70 List.124 List.596; - let List.594 : List U8 = CallByName List.71 List.595 List.125; - ret List.594; +procedure List.4 (List.127, List.128): + let List.599 : U64 = 1i64; + let List.598 : List U8 = CallByName List.70 List.127 List.599; + let List.597 : List U8 = CallByName List.71 List.598 List.128; + ret List.597; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.70 (#Attr.2, #Attr.3): - let List.590 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.590; + let List.593 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.593; procedure List.71 (#Attr.2, #Attr.3): - let List.588 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.588; + let List.591 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.591; procedure List.8 (#Attr.2, #Attr.3): - let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.598; - -procedure List.92 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.70 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.601; + +procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.71 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + jump List.580 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; procedure Num.127 (#Attr.2): - let Num.280 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.280; + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.12 (#Attr.2): - let Str.241 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.241; + let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.245; procedure Str.36 (#Attr.2): - let Str.242 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.242; + let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.246; procedure Str.43 (#Attr.2): - let Str.239 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; - ret Str.239; - -procedure Str.9 (Str.67): - let Str.68 : {U64, Str, Int1, U8} = CallByName Str.43 Str.67; - let Str.236 : Int1 = StructAtIndex 2 Str.68; - if Str.236 then - let Str.238 : Str = StructAtIndex 1 Str.68; - let Str.237 : [C {U64, U8}, C Str] = TagId(1) Str.238; - ret Str.237; + let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + ret Str.243; + +procedure Str.9 (Str.71): + let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71; + let Str.240 : Int1 = StructAtIndex 2 Str.72; + if Str.240 then + let Str.242 : Str = StructAtIndex 1 Str.72; + let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242; + ret Str.241; else - let Str.234 : U8 = StructAtIndex 3 Str.68; - let Str.235 : U64 = StructAtIndex 0 Str.68; - let #Derived_gen.24 : Str = StructAtIndex 1 Str.68; + let Str.238 : U8 = StructAtIndex 3 Str.72; + let Str.239 : U64 = StructAtIndex 0 Str.72; + let #Derived_gen.24 : Str = StructAtIndex 1 Str.72; dec #Derived_gen.24; - let Str.233 : {U64, U8} = Struct {Str.235, Str.234}; - let Str.232 : [C {U64, U8}, C Str] = TagId(0) Str.233; - ret Str.232; - -procedure Test.19 (Test.55): - let Test.291 : Str = CallByName Encode.23 Test.55; - ret Test.291; - -procedure Test.2 (): - let Test.257 : {} = Struct {}; - ret Test.257; - -procedure Test.21 (Test.66): - let Test.259 : List {Str, Str} = CallByName Encode.23 Test.66; - ret Test.259; - -procedure Test.3 (Test.48, Test.49, Test.50): - let Test.288 : U8 = CallByName Num.127 Test.49; - let Test.285 : List U8 = CallByName List.4 Test.48 Test.288; - let Test.287 : Str = CallByName Num.96 Test.50; - let Test.286 : List U8 = CallByName Str.12 Test.287; - let Test.283 : List U8 = CallByName List.8 Test.285 Test.286; - let Test.284 : U8 = 32i64; - let Test.282 : List U8 = CallByName List.4 Test.283 Test.284; - ret Test.282; - -procedure Test.56 (Test.57, Test.273, Test.55): - let Test.280 : I64 = 115i64; - let Test.281 : U64 = CallByName Str.36 Test.55; - let Test.278 : List U8 = CallByName Test.3 Test.57 Test.280 Test.281; - let Test.279 : List U8 = CallByName Str.12 Test.55; - let Test.276 : List U8 = CallByName List.8 Test.278 Test.279; - let Test.277 : U8 = 32i64; - let Test.275 : List U8 = CallByName List.4 Test.276 Test.277; - ret Test.275; - -procedure Test.67 (Test.68, Test.261, Test.66): - let Test.289 : I64 = 114i64; - let Test.290 : U64 = CallByName List.6 Test.66; - let Test.69 : List U8 = CallByName Test.3 Test.68 Test.289 Test.290; - let Test.264 : {} = Struct {}; - let Test.263 : List U8 = CallByName List.18 Test.66 Test.69 Test.264; - ret Test.263; - -procedure Test.70 (Test.71, Test.265): - let Test.72 : Str = StructAtIndex 0 Test.265; - let Test.73 : Str = StructAtIndex 1 Test.265; - let Test.269 : Str = CallByName Test.19 Test.72; - let Test.270 : {} = Struct {}; - let Test.267 : List U8 = CallByName Encode.24 Test.71 Test.269 Test.270; - let Test.268 : {} = Struct {}; - let Test.266 : List U8 = CallByName Encode.24 Test.267 Test.73 Test.268; - ret Test.266; + let Str.237 : {U64, U8} = Struct {Str.239, Str.238}; + let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237; + ret Str.236; + +procedure Test.20 (Test.56): + let Test.292 : Str = CallByName Encode.23 Test.56; + ret Test.292; + +procedure Test.22 (Test.67): + let Test.260 : List {Str, Str} = CallByName Encode.23 Test.67; + ret Test.260; + +procedure Test.3 (): + let Test.258 : {} = Struct {}; + ret Test.258; + +procedure Test.4 (Test.49, Test.50, Test.51): + let Test.289 : U8 = CallByName Num.127 Test.50; + let Test.286 : List U8 = CallByName List.4 Test.49 Test.289; + let Test.288 : Str = CallByName Num.96 Test.51; + let Test.287 : List U8 = CallByName Str.12 Test.288; + let Test.284 : List U8 = CallByName List.8 Test.286 Test.287; + let Test.285 : U8 = 32i64; + let Test.283 : List U8 = CallByName List.4 Test.284 Test.285; + ret Test.283; + +procedure Test.57 (Test.58, Test.274, Test.56): + let Test.281 : I64 = 115i64; + let Test.282 : U64 = CallByName Str.36 Test.56; + let Test.279 : List U8 = CallByName Test.4 Test.58 Test.281 Test.282; + let Test.280 : List U8 = CallByName Str.12 Test.56; + let Test.277 : List U8 = CallByName List.8 Test.279 Test.280; + let Test.278 : U8 = 32i64; + let Test.276 : List U8 = CallByName List.4 Test.277 Test.278; + ret Test.276; + +procedure Test.68 (Test.69, Test.262, Test.67): + let Test.290 : I64 = 114i64; + let Test.291 : U64 = CallByName List.6 Test.67; + let Test.70 : List U8 = CallByName Test.4 Test.69 Test.290 Test.291; + let Test.265 : {} = Struct {}; + let Test.264 : List U8 = CallByName List.18 Test.67 Test.70 Test.265; + ret Test.264; + +procedure Test.71 (Test.72, Test.266): + let Test.73 : Str = StructAtIndex 0 Test.266; + let Test.74 : Str = StructAtIndex 1 Test.266; + let Test.270 : Str = CallByName Test.20 Test.73; + let Test.271 : {} = Struct {}; + let Test.268 : List U8 = CallByName Encode.24 Test.72 Test.270 Test.271; + let Test.269 : {} = Struct {}; + let Test.267 : List U8 = CallByName Encode.24 Test.268 Test.74 Test.269; + ret Test.267; procedure Test.0 (): - let Test.258 : Str = "foo"; - let Test.256 : {} = CallByName Test.2; - let Test.254 : List U8 = CallByName Encode.26 Test.258 Test.256; - let Test.209 : [C {U64, U8}, C Str] = CallByName Str.9 Test.254; - let Test.251 : U8 = 1i64; - let Test.252 : U8 = GetTagId Test.209; - let Test.253 : Int1 = lowlevel Eq Test.251 Test.252; - if Test.253 then - let Test.210 : Str = UnionAtIndex (Id 1) (Index 0) Test.209; - ret Test.210; + let Test.259 : Str = "foo"; + let Test.257 : {} = CallByName Test.3; + let Test.255 : List U8 = CallByName Encode.26 Test.259 Test.257; + let Test.210 : [C {U64, U8}, C Str] = CallByName Str.9 Test.255; + let Test.252 : U8 = 1i64; + let Test.253 : U8 = GetTagId Test.210; + let Test.254 : Int1 = lowlevel Eq Test.252 Test.253; + if Test.254 then + let Test.211 : Str = UnionAtIndex (Id 1) (Index 0) Test.210; + ret Test.211; else - dec Test.209; - let Test.250 : Str = ""; - ret Test.250; + dec Test.210; + let Test.251 : Str = ""; + ret Test.251; diff --git a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt index e29591e8541..be1d3a77674 100644 --- a/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/encode_derived_record_two_field_strings.txt @@ -6,201 +6,201 @@ procedure #Derived.2 (#Derived.3, #Derived.4, #Derived.1): let #Derived_gen.11 : Str = "a"; let #Derived_gen.13 : Str = StructAtIndex 0 #Derived.1; inc #Derived_gen.13; - let #Derived_gen.12 : Str = CallByName Test.19 #Derived_gen.13; + let #Derived_gen.12 : Str = CallByName Test.20 #Derived_gen.13; let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.11, #Derived_gen.12}; let #Derived_gen.8 : Str = "b"; let #Derived_gen.10 : Str = StructAtIndex 1 #Derived.1; dec #Derived_gen.13; - let #Derived_gen.9 : Str = CallByName Test.19 #Derived_gen.10; + let #Derived_gen.9 : Str = CallByName Test.20 #Derived_gen.10; let #Derived_gen.7 : {Str, Str} = Struct {#Derived_gen.8, #Derived_gen.9}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6, #Derived_gen.7]; - let #Derived_gen.4 : List {Str, Str} = CallByName Test.21 #Derived_gen.5; + let #Derived_gen.4 : List {Str, Str} = CallByName Test.22 #Derived_gen.5; let #Derived_gen.3 : List U8 = CallByName Encode.24 #Derived.3 #Derived_gen.4 #Derived.4; ret #Derived_gen.3; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; - ret Encode.111; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.113 : List U8 = CallByName Test.67 Encode.99 Encode.101 Encode.107; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName #Derived.2 Encode.101 Encode.103 Encode.109; ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.119 : List U8 = CallByName Test.56 Encode.99 Encode.101 Encode.107; - ret Encode.119; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.115 : List U8 = CallByName Test.68 Encode.101 Encode.103 Encode.109; + ret Encode.115; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.121 : List U8 = CallByName Test.57 Encode.101 Encode.103 Encode.109; + ret Encode.121; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : {Str, Str} = CallByName #Derived.0 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : {Str, Str} = CallByName #Derived.0 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; -procedure List.4 (List.124, List.125): - let List.596 : U64 = 1i64; - let List.595 : List U8 = CallByName List.70 List.124 List.596; - let List.594 : List U8 = CallByName List.71 List.595 List.125; - ret List.594; +procedure List.4 (List.127, List.128): + let List.599 : U64 = 1i64; + let List.598 : List U8 = CallByName List.70 List.127 List.599; + let List.597 : List U8 = CallByName List.71 List.598 List.128; + ret List.597; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.70 (#Attr.2, #Attr.3): - let List.590 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.590; + let List.593 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.593; procedure List.71 (#Attr.2, #Attr.3): - let List.588 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.588; + let List.591 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.591; procedure List.8 (#Attr.2, #Attr.3): - let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.598; - -procedure List.92 (#Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.70 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.601; + +procedure List.95 (#Derived_gen.17, #Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.71 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21; + jump List.580 #Derived_gen.17 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21; procedure Num.127 (#Attr.2): - let Num.280 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.280; + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.12 (#Attr.2): - let Str.241 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.241; + let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.245; procedure Str.36 (#Attr.2): - let Str.242 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.242; + let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.246; procedure Str.43 (#Attr.2): - let Str.239 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; - ret Str.239; - -procedure Str.9 (Str.67): - let Str.68 : {U64, Str, Int1, U8} = CallByName Str.43 Str.67; - let Str.236 : Int1 = StructAtIndex 2 Str.68; - if Str.236 then - let Str.238 : Str = StructAtIndex 1 Str.68; - let Str.237 : [C {U64, U8}, C Str] = TagId(1) Str.238; - ret Str.237; + let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + ret Str.243; + +procedure Str.9 (Str.71): + let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71; + let Str.240 : Int1 = StructAtIndex 2 Str.72; + if Str.240 then + let Str.242 : Str = StructAtIndex 1 Str.72; + let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242; + ret Str.241; else - let Str.234 : U8 = StructAtIndex 3 Str.68; - let Str.235 : U64 = StructAtIndex 0 Str.68; - let #Derived_gen.28 : Str = StructAtIndex 1 Str.68; + let Str.238 : U8 = StructAtIndex 3 Str.72; + let Str.239 : U64 = StructAtIndex 0 Str.72; + let #Derived_gen.28 : Str = StructAtIndex 1 Str.72; dec #Derived_gen.28; - let Str.233 : {U64, U8} = Struct {Str.235, Str.234}; - let Str.232 : [C {U64, U8}, C Str] = TagId(0) Str.233; - ret Str.232; - -procedure Test.19 (Test.55): - let Test.295 : Str = CallByName Encode.23 Test.55; - ret Test.295; - -procedure Test.2 (): - let Test.257 : {} = Struct {}; - ret Test.257; - -procedure Test.21 (Test.66): - let Test.260 : List {Str, Str} = CallByName Encode.23 Test.66; - ret Test.260; - -procedure Test.3 (Test.48, Test.49, Test.50): - let Test.289 : U8 = CallByName Num.127 Test.49; - let Test.286 : List U8 = CallByName List.4 Test.48 Test.289; - let Test.288 : Str = CallByName Num.96 Test.50; - let Test.287 : List U8 = CallByName Str.12 Test.288; - let Test.284 : List U8 = CallByName List.8 Test.286 Test.287; - let Test.285 : U8 = 32i64; - let Test.283 : List U8 = CallByName List.4 Test.284 Test.285; - ret Test.283; - -procedure Test.56 (Test.57, Test.274, Test.55): - let Test.281 : I64 = 115i64; - let Test.282 : U64 = CallByName Str.36 Test.55; - let Test.279 : List U8 = CallByName Test.3 Test.57 Test.281 Test.282; - let Test.280 : List U8 = CallByName Str.12 Test.55; - let Test.277 : List U8 = CallByName List.8 Test.279 Test.280; - let Test.278 : U8 = 32i64; - let Test.276 : List U8 = CallByName List.4 Test.277 Test.278; - ret Test.276; - -procedure Test.67 (Test.68, Test.262, Test.66): - let Test.290 : I64 = 114i64; - let Test.291 : U64 = CallByName List.6 Test.66; - let Test.69 : List U8 = CallByName Test.3 Test.68 Test.290 Test.291; - let Test.265 : {} = Struct {}; - let Test.264 : List U8 = CallByName List.18 Test.66 Test.69 Test.265; - ret Test.264; - -procedure Test.70 (Test.71, Test.266): - let Test.72 : Str = StructAtIndex 0 Test.266; - let Test.73 : Str = StructAtIndex 1 Test.266; - let Test.270 : Str = CallByName Test.19 Test.72; - let Test.271 : {} = Struct {}; - let Test.268 : List U8 = CallByName Encode.24 Test.71 Test.270 Test.271; - let Test.269 : {} = Struct {}; - let Test.267 : List U8 = CallByName Encode.24 Test.268 Test.73 Test.269; - ret Test.267; + let Str.237 : {U64, U8} = Struct {Str.239, Str.238}; + let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237; + ret Str.236; + +procedure Test.20 (Test.56): + let Test.296 : Str = CallByName Encode.23 Test.56; + ret Test.296; + +procedure Test.22 (Test.67): + let Test.261 : List {Str, Str} = CallByName Encode.23 Test.67; + ret Test.261; + +procedure Test.3 (): + let Test.258 : {} = Struct {}; + ret Test.258; + +procedure Test.4 (Test.49, Test.50, Test.51): + let Test.290 : U8 = CallByName Num.127 Test.50; + let Test.287 : List U8 = CallByName List.4 Test.49 Test.290; + let Test.289 : Str = CallByName Num.96 Test.51; + let Test.288 : List U8 = CallByName Str.12 Test.289; + let Test.285 : List U8 = CallByName List.8 Test.287 Test.288; + let Test.286 : U8 = 32i64; + let Test.284 : List U8 = CallByName List.4 Test.285 Test.286; + ret Test.284; + +procedure Test.57 (Test.58, Test.275, Test.56): + let Test.282 : I64 = 115i64; + let Test.283 : U64 = CallByName Str.36 Test.56; + let Test.280 : List U8 = CallByName Test.4 Test.58 Test.282 Test.283; + let Test.281 : List U8 = CallByName Str.12 Test.56; + let Test.278 : List U8 = CallByName List.8 Test.280 Test.281; + let Test.279 : U8 = 32i64; + let Test.277 : List U8 = CallByName List.4 Test.278 Test.279; + ret Test.277; + +procedure Test.68 (Test.69, Test.263, Test.67): + let Test.291 : I64 = 114i64; + let Test.292 : U64 = CallByName List.6 Test.67; + let Test.70 : List U8 = CallByName Test.4 Test.69 Test.291 Test.292; + let Test.266 : {} = Struct {}; + let Test.265 : List U8 = CallByName List.18 Test.67 Test.70 Test.266; + ret Test.265; + +procedure Test.71 (Test.72, Test.267): + let Test.73 : Str = StructAtIndex 0 Test.267; + let Test.74 : Str = StructAtIndex 1 Test.267; + let Test.271 : Str = CallByName Test.20 Test.73; + let Test.272 : {} = Struct {}; + let Test.269 : List U8 = CallByName Encode.24 Test.72 Test.271 Test.272; + let Test.270 : {} = Struct {}; + let Test.268 : List U8 = CallByName Encode.24 Test.269 Test.74 Test.270; + ret Test.268; procedure Test.0 (): - let Test.258 : Str = "foo"; - let Test.259 : Str = "bar"; - let Test.255 : {Str, Str} = Struct {Test.258, Test.259}; - let Test.256 : {} = CallByName Test.2; - let Test.254 : List U8 = CallByName Encode.26 Test.255 Test.256; - let Test.209 : [C {U64, U8}, C Str] = CallByName Str.9 Test.254; - let Test.251 : U8 = 1i64; - let Test.252 : U8 = GetTagId Test.209; - let Test.253 : Int1 = lowlevel Eq Test.251 Test.252; - if Test.253 then - let Test.210 : Str = UnionAtIndex (Id 1) (Index 0) Test.209; - ret Test.210; + let Test.259 : Str = "foo"; + let Test.260 : Str = "bar"; + let Test.256 : {Str, Str} = Struct {Test.259, Test.260}; + let Test.257 : {} = CallByName Test.3; + let Test.255 : List U8 = CallByName Encode.26 Test.256 Test.257; + let Test.210 : [C {U64, U8}, C Str] = CallByName Str.9 Test.255; + let Test.252 : U8 = 1i64; + let Test.253 : U8 = GetTagId Test.210; + let Test.254 : Int1 = lowlevel Eq Test.252 Test.253; + if Test.254 then + let Test.211 : Str = UnionAtIndex (Id 1) (Index 0) Test.210; + ret Test.211; else - dec Test.209; - let Test.250 : Str = ""; - ret Test.250; + dec Test.210; + let Test.251 : Str = ""; + ret Test.251; diff --git a/crates/compiler/test_mono/generated/encode_derived_string.txt b/crates/compiler/test_mono/generated/encode_derived_string.txt index 7e0c164ae4c..818233a6973 100644 --- a/crates/compiler/test_mono/generated/encode_derived_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_string.txt @@ -1,110 +1,110 @@ -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName Test.56 Encode.99 Encode.101 Encode.107; - ret Encode.111; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName Test.57 Encode.101 Encode.103 Encode.109; + ret Encode.113; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : Str = CallByName Test.19 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : Str = CallByName Test.20 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; -procedure List.4 (List.124, List.125): - let List.584 : U64 = 1i64; - let List.583 : List U8 = CallByName List.70 List.124 List.584; - let List.582 : List U8 = CallByName List.71 List.583 List.125; - ret List.582; +procedure List.4 (List.127, List.128): + let List.587 : U64 = 1i64; + let List.586 : List U8 = CallByName List.70 List.127 List.587; + let List.585 : List U8 = CallByName List.71 List.586 List.128; + ret List.585; procedure List.70 (#Attr.2, #Attr.3): - let List.578 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.578; + let List.581 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.581; procedure List.71 (#Attr.2, #Attr.3): - let List.576 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.576; + let List.579 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.579; procedure List.8 (#Attr.2, #Attr.3): - let List.586 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.586; + let List.589 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.589; procedure Num.127 (#Attr.2): - let Num.280 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.280; + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.12 (#Attr.2): - let Str.241 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.241; + let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.245; procedure Str.36 (#Attr.2): - let Str.242 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.242; + let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.246; procedure Str.43 (#Attr.2): - let Str.239 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; - ret Str.239; - -procedure Str.9 (Str.67): - let Str.68 : {U64, Str, Int1, U8} = CallByName Str.43 Str.67; - let Str.236 : Int1 = StructAtIndex 2 Str.68; - if Str.236 then - let Str.238 : Str = StructAtIndex 1 Str.68; - let Str.237 : [C {U64, U8}, C Str] = TagId(1) Str.238; - ret Str.237; + let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + ret Str.243; + +procedure Str.9 (Str.71): + let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71; + let Str.240 : Int1 = StructAtIndex 2 Str.72; + if Str.240 then + let Str.242 : Str = StructAtIndex 1 Str.72; + let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242; + ret Str.241; else - let Str.234 : U8 = StructAtIndex 3 Str.68; - let Str.235 : U64 = StructAtIndex 0 Str.68; - let #Derived_gen.3 : Str = StructAtIndex 1 Str.68; + let Str.238 : U8 = StructAtIndex 3 Str.72; + let Str.239 : U64 = StructAtIndex 0 Str.72; + let #Derived_gen.3 : Str = StructAtIndex 1 Str.72; dec #Derived_gen.3; - let Str.233 : {U64, U8} = Struct {Str.235, Str.234}; - let Str.232 : [C {U64, U8}, C Str] = TagId(0) Str.233; - ret Str.232; + let Str.237 : {U64, U8} = Struct {Str.239, Str.238}; + let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237; + ret Str.236; -procedure Test.19 (Test.55): - let Test.258 : Str = CallByName Encode.23 Test.55; +procedure Test.20 (Test.56): + let Test.259 : Str = CallByName Encode.23 Test.56; + ret Test.259; + +procedure Test.3 (): + let Test.258 : {} = Struct {}; ret Test.258; -procedure Test.2 (): - let Test.257 : {} = Struct {}; - ret Test.257; - -procedure Test.3 (Test.48, Test.49, Test.50): - let Test.275 : U8 = CallByName Num.127 Test.49; - let Test.272 : List U8 = CallByName List.4 Test.48 Test.275; - let Test.274 : Str = CallByName Num.96 Test.50; - let Test.273 : List U8 = CallByName Str.12 Test.274; - let Test.270 : List U8 = CallByName List.8 Test.272 Test.273; - let Test.271 : U8 = 32i64; - let Test.269 : List U8 = CallByName List.4 Test.270 Test.271; - ret Test.269; - -procedure Test.56 (Test.57, Test.260, Test.55): - let Test.267 : I64 = 115i64; - let Test.268 : U64 = CallByName Str.36 Test.55; - let Test.265 : List U8 = CallByName Test.3 Test.57 Test.267 Test.268; - let Test.266 : List U8 = CallByName Str.12 Test.55; - let Test.263 : List U8 = CallByName List.8 Test.265 Test.266; - let Test.264 : U8 = 32i64; - let Test.262 : List U8 = CallByName List.4 Test.263 Test.264; - ret Test.262; +procedure Test.4 (Test.49, Test.50, Test.51): + let Test.276 : U8 = CallByName Num.127 Test.50; + let Test.273 : List U8 = CallByName List.4 Test.49 Test.276; + let Test.275 : Str = CallByName Num.96 Test.51; + let Test.274 : List U8 = CallByName Str.12 Test.275; + let Test.271 : List U8 = CallByName List.8 Test.273 Test.274; + let Test.272 : U8 = 32i64; + let Test.270 : List U8 = CallByName List.4 Test.271 Test.272; + ret Test.270; + +procedure Test.57 (Test.58, Test.261, Test.56): + let Test.268 : I64 = 115i64; + let Test.269 : U64 = CallByName Str.36 Test.56; + let Test.266 : List U8 = CallByName Test.4 Test.58 Test.268 Test.269; + let Test.267 : List U8 = CallByName Str.12 Test.56; + let Test.264 : List U8 = CallByName List.8 Test.266 Test.267; + let Test.265 : U8 = 32i64; + let Test.263 : List U8 = CallByName List.4 Test.264 Test.265; + ret Test.263; procedure Test.0 (): - let Test.255 : Str = "abc"; - let Test.256 : {} = CallByName Test.2; - let Test.254 : List U8 = CallByName Encode.26 Test.255 Test.256; - let Test.209 : [C {U64, U8}, C Str] = CallByName Str.9 Test.254; - let Test.251 : U8 = 1i64; - let Test.252 : U8 = GetTagId Test.209; - let Test.253 : Int1 = lowlevel Eq Test.251 Test.252; - if Test.253 then - let Test.210 : Str = UnionAtIndex (Id 1) (Index 0) Test.209; - ret Test.210; + let Test.256 : Str = "abc"; + let Test.257 : {} = CallByName Test.3; + let Test.255 : List U8 = CallByName Encode.26 Test.256 Test.257; + let Test.210 : [C {U64, U8}, C Str] = CallByName Str.9 Test.255; + let Test.252 : U8 = 1i64; + let Test.253 : U8 = GetTagId Test.210; + let Test.254 : Int1 = lowlevel Eq Test.252 Test.253; + if Test.254 then + let Test.211 : Str = UnionAtIndex (Id 1) (Index 0) Test.210; + ret Test.211; else - dec Test.209; - let Test.250 : Str = ""; - ret Test.250; + dec Test.210; + let Test.251 : Str = ""; + ret Test.251; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt index 884b2de4a3d..b01ebbab907 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_one_field_string.txt @@ -8,208 +8,208 @@ procedure #Derived.3 (#Derived.4, #Derived.5, #Derived.1): ret #Derived_gen.3; in let #Derived_gen.7 : Str = "A"; - let #Derived_gen.9 : Str = CallByName Test.19 #Derived.1; + let #Derived_gen.9 : Str = CallByName Test.20 #Derived.1; let #Derived_gen.8 : List Str = Array [#Derived_gen.9]; - let #Derived_gen.6 : {List Str, {}} = CallByName Test.23 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : {List Str, {}} = CallByName Test.24 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName #Derived.3 Encode.99 Encode.101 Encode.107; - ret Encode.111; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.113 : List U8 = CallByName Test.60 Encode.99 Encode.101 Encode.107; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName #Derived.3 Encode.101 Encode.103 Encode.109; ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.117 : List U8 = CallByName Test.56 Encode.99 Encode.101 Encode.107; - ret Encode.117; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.115 : List U8 = CallByName Test.61 Encode.101 Encode.103 Encode.109; + ret Encode.115; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.119 : List U8 = CallByName Test.57 Encode.101 Encode.103 Encode.109; + ret Encode.119; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : Str = CallByName #Derived.0 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : Str = CallByName #Derived.0 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.600 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.600; + let List.603 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.603; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; -procedure List.4 (List.124, List.125): - let List.596 : U64 = 1i64; - let List.595 : List U8 = CallByName List.70 List.124 List.596; - let List.594 : List U8 = CallByName List.71 List.595 List.125; - ret List.594; +procedure List.4 (List.127, List.128): + let List.599 : U64 = 1i64; + let List.598 : List U8 = CallByName List.70 List.127 List.599; + let List.597 : List U8 = CallByName List.71 List.598 List.128; + ret List.597; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.70 (#Attr.2, #Attr.3): - let List.590 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.590; + let List.593 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.593; procedure List.71 (#Attr.2, #Attr.3): - let List.588 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.588; + let List.591 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.591; procedure List.8 (#Attr.2, #Attr.3): - let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.598; - -procedure List.92 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : Str = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.63 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.601; + +procedure List.95 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : Str = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.64 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + jump List.580 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14; procedure Num.127 (#Attr.2): - let Num.280 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.280; + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.12 (#Attr.2): - let Str.241 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.241; + let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.245; procedure Str.36 (#Attr.2): - let Str.242 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.242; + let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.246; procedure Str.43 (#Attr.2): - let Str.239 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; - ret Str.239; - -procedure Str.9 (Str.67): - let Str.68 : {U64, Str, Int1, U8} = CallByName Str.43 Str.67; - let Str.236 : Int1 = StructAtIndex 2 Str.68; - if Str.236 then - let Str.238 : Str = StructAtIndex 1 Str.68; - let Str.237 : [C {U64, U8}, C Str] = TagId(1) Str.238; - ret Str.237; + let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + ret Str.243; + +procedure Str.9 (Str.71): + let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71; + let Str.240 : Int1 = StructAtIndex 2 Str.72; + if Str.240 then + let Str.242 : Str = StructAtIndex 1 Str.72; + let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242; + ret Str.241; else - let Str.234 : U8 = StructAtIndex 3 Str.68; - let Str.235 : U64 = StructAtIndex 0 Str.68; - let #Derived_gen.27 : Str = StructAtIndex 1 Str.68; + let Str.238 : U8 = StructAtIndex 3 Str.72; + let Str.239 : U64 = StructAtIndex 0 Str.72; + let #Derived_gen.27 : Str = StructAtIndex 1 Str.72; dec #Derived_gen.27; - let Str.233 : {U64, U8} = Struct {Str.235, Str.234}; - let Str.232 : [C {U64, U8}, C Str] = TagId(0) Str.233; - ret Str.232; - -procedure Test.19 (Test.55): - let Test.296 : Str = CallByName Encode.23 Test.55; - ret Test.296; - -procedure Test.2 (): - let Test.258 : {} = Struct {}; - ret Test.258; - -procedure Test.20 (Test.58, Test.59): - let Test.265 : {List Str, {}} = Struct {Test.58, Test.59}; - let Test.264 : {List Str, {}} = CallByName Encode.23 Test.265; - ret Test.264; - -procedure Test.22 (Test.74): - let Test.263 : {} = Struct {}; - let Test.262 : {List Str, {}} = CallByName Test.20 Test.74 Test.263; - ret Test.262; - -procedure Test.23 (Test.77, Test.78): - let Test.284 : Str = CallByName Test.19 Test.77; - let Test.261 : List Str = CallByName List.13 Test.78 Test.284; - let Test.260 : {List Str, {}} = CallByName Test.22 Test.261; - ret Test.260; - -procedure Test.3 (Test.48, Test.49, Test.50): - let Test.282 : U8 = CallByName Num.127 Test.49; - let Test.279 : List U8 = CallByName List.4 Test.48 Test.282; - let Test.281 : Str = CallByName Num.96 Test.50; - let Test.280 : List U8 = CallByName Str.12 Test.281; - let Test.277 : List U8 = CallByName List.8 Test.279 Test.280; - let Test.278 : U8 = 32i64; - let Test.276 : List U8 = CallByName List.4 Test.277 Test.278; - ret Test.276; - -procedure Test.56 (Test.57, Test.287, Test.55): - let Test.294 : I64 = 115i64; - let Test.295 : U64 = CallByName Str.36 Test.55; - let Test.292 : List U8 = CallByName Test.3 Test.57 Test.294 Test.295; - let Test.293 : List U8 = CallByName Str.12 Test.55; - let Test.290 : List U8 = CallByName List.8 Test.292 Test.293; - let Test.291 : U8 = 32i64; - let Test.289 : List U8 = CallByName List.4 Test.290 Test.291; - ret Test.289; - -procedure Test.60 (Test.61, Test.266, #Attr.12): - let Test.59 : {} = StructAtIndex 1 #Attr.12; - let Test.58 : List Str = StructAtIndex 0 #Attr.12; - let Test.274 : I64 = 108i64; - let Test.275 : U64 = CallByName List.6 Test.58; - let Test.62 : List U8 = CallByName Test.3 Test.61 Test.274 Test.275; - let Test.268 : List U8 = CallByName List.18 Test.58 Test.62 Test.59; - ret Test.268; - -procedure Test.63 (Test.64, Test.65, Test.59): - let Test.272 : Str = CallByName Test.75 Test.65; - let Test.273 : {} = Struct {}; - let Test.271 : List U8 = CallByName Encode.24 Test.64 Test.272 Test.273; - ret Test.271; - -procedure Test.75 (Test.76): - ret Test.76; + let Str.237 : {U64, U8} = Struct {Str.239, Str.238}; + let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237; + ret Str.236; + +procedure Test.20 (Test.56): + let Test.297 : Str = CallByName Encode.23 Test.56; + ret Test.297; + +procedure Test.21 (Test.59, Test.60): + let Test.266 : {List Str, {}} = Struct {Test.59, Test.60}; + let Test.265 : {List Str, {}} = CallByName Encode.23 Test.266; + ret Test.265; + +procedure Test.23 (Test.75): + let Test.264 : {} = Struct {}; + let Test.263 : {List Str, {}} = CallByName Test.21 Test.75 Test.264; + ret Test.263; + +procedure Test.24 (Test.78, Test.79): + let Test.285 : Str = CallByName Test.20 Test.78; + let Test.262 : List Str = CallByName List.13 Test.79 Test.285; + let Test.261 : {List Str, {}} = CallByName Test.23 Test.262; + ret Test.261; + +procedure Test.3 (): + let Test.259 : {} = Struct {}; + ret Test.259; + +procedure Test.4 (Test.49, Test.50, Test.51): + let Test.283 : U8 = CallByName Num.127 Test.50; + let Test.280 : List U8 = CallByName List.4 Test.49 Test.283; + let Test.282 : Str = CallByName Num.96 Test.51; + let Test.281 : List U8 = CallByName Str.12 Test.282; + let Test.278 : List U8 = CallByName List.8 Test.280 Test.281; + let Test.279 : U8 = 32i64; + let Test.277 : List U8 = CallByName List.4 Test.278 Test.279; + ret Test.277; + +procedure Test.57 (Test.58, Test.288, Test.56): + let Test.295 : I64 = 115i64; + let Test.296 : U64 = CallByName Str.36 Test.56; + let Test.293 : List U8 = CallByName Test.4 Test.58 Test.295 Test.296; + let Test.294 : List U8 = CallByName Str.12 Test.56; + let Test.291 : List U8 = CallByName List.8 Test.293 Test.294; + let Test.292 : U8 = 32i64; + let Test.290 : List U8 = CallByName List.4 Test.291 Test.292; + ret Test.290; + +procedure Test.61 (Test.62, Test.267, #Attr.12): + let Test.60 : {} = StructAtIndex 1 #Attr.12; + let Test.59 : List Str = StructAtIndex 0 #Attr.12; + let Test.275 : I64 = 108i64; + let Test.276 : U64 = CallByName List.6 Test.59; + let Test.63 : List U8 = CallByName Test.4 Test.62 Test.275 Test.276; + let Test.269 : List U8 = CallByName List.18 Test.59 Test.63 Test.60; + ret Test.269; + +procedure Test.64 (Test.65, Test.66, Test.60): + let Test.273 : Str = CallByName Test.76 Test.66; + let Test.274 : {} = Struct {}; + let Test.272 : List U8 = CallByName Encode.24 Test.65 Test.273 Test.274; + ret Test.272; + +procedure Test.76 (Test.77): + ret Test.77; procedure Test.0 (): - let Test.259 : Str = "foo"; - let Test.257 : {} = CallByName Test.2; - let Test.256 : List U8 = CallByName Encode.26 Test.259 Test.257; - let Test.210 : [C {U64, U8}, C Str] = CallByName Str.9 Test.256; - let Test.253 : U8 = 1i64; - let Test.254 : U8 = GetTagId Test.210; - let Test.255 : Int1 = lowlevel Eq Test.253 Test.254; - if Test.255 then - let Test.212 : Str = UnionAtIndex (Id 1) (Index 0) Test.210; - ret Test.212; + let Test.260 : Str = "foo"; + let Test.258 : {} = CallByName Test.3; + let Test.257 : List U8 = CallByName Encode.26 Test.260 Test.258; + let Test.211 : [C {U64, U8}, C Str] = CallByName Str.9 Test.257; + let Test.254 : U8 = 1i64; + let Test.255 : U8 = GetTagId Test.211; + let Test.256 : Int1 = lowlevel Eq Test.254 Test.255; + if Test.256 then + let Test.213 : Str = UnionAtIndex (Id 1) (Index 0) Test.211; + ret Test.213; else - dec Test.210; - let Test.252 : Str = ""; - ret Test.252; + dec Test.211; + let Test.253 : Str = ""; + ret Test.253; diff --git a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt index 2fd445f3039..57d271de919 100644 --- a/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/encode_derived_tag_two_payloads_string.txt @@ -10,211 +10,211 @@ procedure #Derived.4 (#Derived.5, #Derived.6, #Derived.1): let #Derived.2 : Str = StructAtIndex 0 #Derived.1; let #Derived.3 : Str = StructAtIndex 1 #Derived.1; let #Derived_gen.7 : Str = "A"; - let #Derived_gen.9 : Str = CallByName Test.19 #Derived.2; - let #Derived_gen.10 : Str = CallByName Test.19 #Derived.3; + let #Derived_gen.9 : Str = CallByName Test.20 #Derived.2; + let #Derived_gen.10 : Str = CallByName Test.20 #Derived.3; let #Derived_gen.8 : List Str = Array [#Derived_gen.9, #Derived_gen.10]; - let #Derived_gen.6 : {List Str, {}} = CallByName Test.23 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : {List Str, {}} = CallByName Test.24 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName #Derived.4 Encode.99 Encode.101 Encode.107; - ret Encode.111; - -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.113 : List U8 = CallByName Test.60 Encode.99 Encode.101 Encode.107; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName #Derived.4 Encode.101 Encode.103 Encode.109; ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.118 : List U8 = CallByName Test.56 Encode.99 Encode.101 Encode.107; - ret Encode.118; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.115 : List U8 = CallByName Test.61 Encode.101 Encode.103 Encode.109; + ret Encode.115; + +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.120 : List U8 = CallByName Test.57 Encode.101 Encode.103 Encode.109; + ret Encode.120; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : {Str, Str} = CallByName #Derived.0 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : {Str, Str} = CallByName #Derived.0 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.600 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.600; + let List.603 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.603; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; -procedure List.4 (List.124, List.125): - let List.596 : U64 = 1i64; - let List.595 : List U8 = CallByName List.70 List.124 List.596; - let List.594 : List U8 = CallByName List.71 List.595 List.125; - ret List.594; +procedure List.4 (List.127, List.128): + let List.599 : U64 = 1i64; + let List.598 : List U8 = CallByName List.70 List.127 List.599; + let List.597 : List U8 = CallByName List.71 List.598 List.128; + ret List.597; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.70 (#Attr.2, #Attr.3): - let List.590 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.590; + let List.593 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.593; procedure List.71 (#Attr.2, #Attr.3): - let List.588 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.588; + let List.591 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.591; procedure List.8 (#Attr.2, #Attr.3): - let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.598; - -procedure List.92 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : Str = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.63 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.601; + +procedure List.95 (#Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14, #Derived_gen.15): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : Str = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.64 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; + jump List.580 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15; procedure Num.127 (#Attr.2): - let Num.280 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.280; + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.12 (#Attr.2): - let Str.241 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.241; + let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.245; procedure Str.36 (#Attr.2): - let Str.242 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.242; + let Str.246 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.246; procedure Str.43 (#Attr.2): - let Str.239 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; - ret Str.239; - -procedure Str.9 (Str.67): - let Str.68 : {U64, Str, Int1, U8} = CallByName Str.43 Str.67; - let Str.236 : Int1 = StructAtIndex 2 Str.68; - if Str.236 then - let Str.238 : Str = StructAtIndex 1 Str.68; - let Str.237 : [C {U64, U8}, C Str] = TagId(1) Str.238; - ret Str.237; + let Str.243 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8 #Attr.2; + ret Str.243; + +procedure Str.9 (Str.71): + let Str.72 : {U64, Str, Int1, U8} = CallByName Str.43 Str.71; + let Str.240 : Int1 = StructAtIndex 2 Str.72; + if Str.240 then + let Str.242 : Str = StructAtIndex 1 Str.72; + let Str.241 : [C {U64, U8}, C Str] = TagId(1) Str.242; + ret Str.241; else - let Str.234 : U8 = StructAtIndex 3 Str.68; - let Str.235 : U64 = StructAtIndex 0 Str.68; - let #Derived_gen.28 : Str = StructAtIndex 1 Str.68; + let Str.238 : U8 = StructAtIndex 3 Str.72; + let Str.239 : U64 = StructAtIndex 0 Str.72; + let #Derived_gen.28 : Str = StructAtIndex 1 Str.72; dec #Derived_gen.28; - let Str.233 : {U64, U8} = Struct {Str.235, Str.234}; - let Str.232 : [C {U64, U8}, C Str] = TagId(0) Str.233; - ret Str.232; - -procedure Test.19 (Test.55): - let Test.300 : Str = CallByName Encode.23 Test.55; - ret Test.300; - -procedure Test.2 (): - let Test.258 : {} = Struct {}; - ret Test.258; - -procedure Test.20 (Test.58, Test.59): - let Test.266 : {List Str, {}} = Struct {Test.58, Test.59}; - let Test.265 : {List Str, {}} = CallByName Encode.23 Test.266; - ret Test.265; - -procedure Test.22 (Test.74): - let Test.264 : {} = Struct {}; - let Test.263 : {List Str, {}} = CallByName Test.20 Test.74 Test.264; - ret Test.263; - -procedure Test.23 (Test.77, Test.78): - let Test.285 : Str = CallByName Test.19 Test.77; - let Test.262 : List Str = CallByName List.13 Test.78 Test.285; - let Test.261 : {List Str, {}} = CallByName Test.22 Test.262; - ret Test.261; - -procedure Test.3 (Test.48, Test.49, Test.50): - let Test.283 : U8 = CallByName Num.127 Test.49; - let Test.280 : List U8 = CallByName List.4 Test.48 Test.283; - let Test.282 : Str = CallByName Num.96 Test.50; - let Test.281 : List U8 = CallByName Str.12 Test.282; - let Test.278 : List U8 = CallByName List.8 Test.280 Test.281; - let Test.279 : U8 = 32i64; - let Test.277 : List U8 = CallByName List.4 Test.278 Test.279; - ret Test.277; - -procedure Test.56 (Test.57, Test.288, Test.55): - let Test.295 : I64 = 115i64; - let Test.296 : U64 = CallByName Str.36 Test.55; - let Test.293 : List U8 = CallByName Test.3 Test.57 Test.295 Test.296; - let Test.294 : List U8 = CallByName Str.12 Test.55; - let Test.291 : List U8 = CallByName List.8 Test.293 Test.294; - let Test.292 : U8 = 32i64; - let Test.290 : List U8 = CallByName List.4 Test.291 Test.292; - ret Test.290; - -procedure Test.60 (Test.61, Test.267, #Attr.12): - let Test.59 : {} = StructAtIndex 1 #Attr.12; - let Test.58 : List Str = StructAtIndex 0 #Attr.12; - let Test.275 : I64 = 108i64; - let Test.276 : U64 = CallByName List.6 Test.58; - let Test.62 : List U8 = CallByName Test.3 Test.61 Test.275 Test.276; - let Test.269 : List U8 = CallByName List.18 Test.58 Test.62 Test.59; - ret Test.269; - -procedure Test.63 (Test.64, Test.65, Test.59): - let Test.273 : Str = CallByName Test.75 Test.65; - let Test.274 : {} = Struct {}; - let Test.272 : List U8 = CallByName Encode.24 Test.64 Test.273 Test.274; - ret Test.272; - -procedure Test.75 (Test.76): - ret Test.76; + let Str.237 : {U64, U8} = Struct {Str.239, Str.238}; + let Str.236 : [C {U64, U8}, C Str] = TagId(0) Str.237; + ret Str.236; + +procedure Test.20 (Test.56): + let Test.301 : Str = CallByName Encode.23 Test.56; + ret Test.301; + +procedure Test.21 (Test.59, Test.60): + let Test.267 : {List Str, {}} = Struct {Test.59, Test.60}; + let Test.266 : {List Str, {}} = CallByName Encode.23 Test.267; + ret Test.266; + +procedure Test.23 (Test.75): + let Test.265 : {} = Struct {}; + let Test.264 : {List Str, {}} = CallByName Test.21 Test.75 Test.265; + ret Test.264; + +procedure Test.24 (Test.78, Test.79): + let Test.286 : Str = CallByName Test.20 Test.78; + let Test.263 : List Str = CallByName List.13 Test.79 Test.286; + let Test.262 : {List Str, {}} = CallByName Test.23 Test.263; + ret Test.262; + +procedure Test.3 (): + let Test.259 : {} = Struct {}; + ret Test.259; + +procedure Test.4 (Test.49, Test.50, Test.51): + let Test.284 : U8 = CallByName Num.127 Test.50; + let Test.281 : List U8 = CallByName List.4 Test.49 Test.284; + let Test.283 : Str = CallByName Num.96 Test.51; + let Test.282 : List U8 = CallByName Str.12 Test.283; + let Test.279 : List U8 = CallByName List.8 Test.281 Test.282; + let Test.280 : U8 = 32i64; + let Test.278 : List U8 = CallByName List.4 Test.279 Test.280; + ret Test.278; + +procedure Test.57 (Test.58, Test.289, Test.56): + let Test.296 : I64 = 115i64; + let Test.297 : U64 = CallByName Str.36 Test.56; + let Test.294 : List U8 = CallByName Test.4 Test.58 Test.296 Test.297; + let Test.295 : List U8 = CallByName Str.12 Test.56; + let Test.292 : List U8 = CallByName List.8 Test.294 Test.295; + let Test.293 : U8 = 32i64; + let Test.291 : List U8 = CallByName List.4 Test.292 Test.293; + ret Test.291; + +procedure Test.61 (Test.62, Test.268, #Attr.12): + let Test.60 : {} = StructAtIndex 1 #Attr.12; + let Test.59 : List Str = StructAtIndex 0 #Attr.12; + let Test.276 : I64 = 108i64; + let Test.277 : U64 = CallByName List.6 Test.59; + let Test.63 : List U8 = CallByName Test.4 Test.62 Test.276 Test.277; + let Test.270 : List U8 = CallByName List.18 Test.59 Test.63 Test.60; + ret Test.270; + +procedure Test.64 (Test.65, Test.66, Test.60): + let Test.274 : Str = CallByName Test.76 Test.66; + let Test.275 : {} = Struct {}; + let Test.273 : List U8 = CallByName Encode.24 Test.65 Test.274 Test.275; + ret Test.273; + +procedure Test.76 (Test.77): + ret Test.77; procedure Test.0 (): + let Test.261 : Str = "foo"; let Test.260 : Str = "foo"; - let Test.259 : Str = "foo"; - let Test.209 : {Str, Str} = Struct {Test.259, Test.260}; - let Test.257 : {} = CallByName Test.2; - let Test.256 : List U8 = CallByName Encode.26 Test.209 Test.257; - let Test.210 : [C {U64, U8}, C Str] = CallByName Str.9 Test.256; - let Test.253 : U8 = 1i64; - let Test.254 : U8 = GetTagId Test.210; - let Test.255 : Int1 = lowlevel Eq Test.253 Test.254; - if Test.255 then - let Test.212 : Str = UnionAtIndex (Id 1) (Index 0) Test.210; - ret Test.212; + let Test.210 : {Str, Str} = Struct {Test.260, Test.261}; + let Test.258 : {} = CallByName Test.3; + let Test.257 : List U8 = CallByName Encode.26 Test.210 Test.258; + let Test.211 : [C {U64, U8}, C Str] = CallByName Str.9 Test.257; + let Test.254 : U8 = 1i64; + let Test.255 : U8 = GetTagId Test.211; + let Test.256 : Int1 = lowlevel Eq Test.254 Test.255; + if Test.256 then + let Test.213 : Str = UnionAtIndex (Id 1) (Index 0) Test.211; + ret Test.213; else - dec Test.210; - let Test.252 : Str = ""; - ret Test.252; + dec Test.211; + let Test.253 : Str = ""; + ret Test.253; diff --git a/crates/compiler/test_mono/generated/factorial.txt b/crates/compiler/test_mono/generated/factorial.txt index 19d5d2862ff..a9655ad62d4 100644 --- a/crates/compiler/test_mono/generated/factorial.txt +++ b/crates/compiler/test_mono/generated/factorial.txt @@ -1,10 +1,10 @@ procedure Num.20 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.282; procedure Num.21 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.7 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk.txt b/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk.txt index 59c8a2a93c6..ec22d853c60 100644 --- a/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk.txt +++ b/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.8): let Test.3 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk_independent_defs.txt b/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk_independent_defs.txt index 4b360b65e67..96fa433e561 100644 --- a/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk_independent_defs.txt +++ b/crates/compiler/test_mono/generated/function_specialization_information_in_lambda_set_thunk_independent_defs.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Test.1 (Test.9): let Test.4 : U8 = 10i64; diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt index 0e35e619a69..0e45e1077e2 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_bool_lambda_set.txt @@ -3,8 +3,8 @@ procedure Bool.1 (): ret Bool.23; procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.3 (Test.4): ret Test.4; diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt index 4215c458051..8cd25bf3901 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_enum_lambda_set.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.283; procedure Test.2 (Test.3): switch Test.3: diff --git a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt index 9451b2635d1..575a74f1db9 100644 --- a/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt +++ b/crates/compiler/test_mono/generated/inline_return_joinpoints_in_union_lambda_set.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Test.2 (Test.3, Test.1): let Test.18 : Int1 = false; diff --git a/crates/compiler/test_mono/generated/inspect_derived_dict.txt b/crates/compiler/test_mono/generated/inspect_derived_dict.txt index 99c79d9622f..6fbd4423c0c 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_dict.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_dict.txt @@ -34,1149 +34,1149 @@ procedure Bool.7 (Bool.19, Bool.20): let Bool.27 : Int1 = CallByName Bool.12 Bool.19 Bool.20; ret Bool.27; -procedure Dict.1 (Dict.723): - let Dict.885 : List {U32, U32} = Array []; - let Dict.886 : List {Str, I64} = Array []; - let Dict.887 : U64 = 0i64; - let Dict.44 : Float32 = CallByName Dict.44; - let Dict.45 : U8 = CallByName Dict.45; - let Dict.884 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.885, Dict.886, Dict.887, Dict.44, Dict.45}; - ret Dict.884; - -procedure Dict.10 (Dict.724, Dict.179, Dict.180): - let Dict.178 : List {Str, I64} = StructAtIndex 1 Dict.724; - let #Derived_gen.68 : List {U32, U32} = StructAtIndex 0 Dict.724; - dec #Derived_gen.68; - let Dict.1101 : {Str, Int1} = CallByName List.18 Dict.178 Dict.179 Dict.180; - ret Dict.1101; - -procedure Dict.12 (Dict.151): - let Dict.883 : {} = Struct {}; - let Dict.731 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.1 Dict.883; - let Dict.732 : {} = Struct {}; - let Dict.730 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName List.18 Dict.151 Dict.731 Dict.732; - ret Dict.730; - -procedure Dict.120 (Dict.121, Dict.119): - let Dict.1098 : {} = Struct {}; - let Dict.1099 : {} = Struct {}; - let Dict.1100 : {} = Struct {}; - let Dict.1097 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = CallByName Inspect.38 Dict.119 Dict.1098 Dict.1099 Dict.1100; - let Dict.1096 : Str = CallByName Inspect.31 Dict.1097 Dict.121; - ret Dict.1096; - -procedure Dict.152 (Dict.153, Dict.733): - let Dict.154 : Str = StructAtIndex 0 Dict.733; - let Dict.155 : I64 = StructAtIndex 1 Dict.733; - let Dict.734 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.8 Dict.153 Dict.154 Dict.155; - ret Dict.734; - -procedure Dict.181 (Dict.182, Dict.1103, Dict.180): - let Dict.183 : Str = StructAtIndex 0 Dict.1103; - let Dict.184 : I64 = StructAtIndex 1 Dict.1103; - let Dict.1105 : {Str, Int1} = CallByName Inspect.187 Dict.182 Dict.183 Dict.184 Dict.180; - ret Dict.1105; - -procedure Dict.20 (Dict.720): - let Dict.148 : U64 = StructAtIndex 2 Dict.720; - let #Derived_gen.70 : List {U32, U32} = StructAtIndex 0 Dict.720; +procedure Dict.1 (Dict.730): + let Dict.892 : List {U32, U32} = Array []; + let Dict.893 : List {Str, I64} = Array []; + let Dict.894 : U64 = 0i64; + let Dict.51 : Float32 = CallByName Dict.51; + let Dict.52 : U8 = CallByName Dict.52; + let Dict.891 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.892, Dict.893, Dict.894, Dict.51, Dict.52}; + ret Dict.891; + +procedure Dict.10 (Dict.731, Dict.186, Dict.187): + let Dict.185 : List {Str, I64} = StructAtIndex 1 Dict.731; + let #Derived_gen.67 : List {U32, U32} = StructAtIndex 0 Dict.731; + dec #Derived_gen.67; + let Dict.1108 : {Str, Int1} = CallByName List.18 Dict.185 Dict.186 Dict.187; + ret Dict.1108; + +procedure Dict.100 (Dict.544, Dict.545, Dict.546): + let Dict.1061 : U8 = CallByName Dict.22 Dict.544 Dict.545; + let Dict.547 : U64 = CallByName Num.133 Dict.1061; + let Dict.1060 : U8 = 1i64; + let Dict.1059 : U64 = CallByName Num.74 Dict.546 Dict.1060; + let Dict.1058 : U64 = CallByName Num.51 Dict.1059 Dict.545; + let Dict.1057 : U8 = CallByName Dict.22 Dict.544 Dict.1058; + let Dict.548 : U64 = CallByName Num.133 Dict.1057; + let Dict.1056 : U64 = 1i64; + let Dict.1055 : U64 = CallByName Num.75 Dict.546 Dict.1056; + let Dict.1054 : U64 = CallByName Num.51 Dict.1055 Dict.545; + let Dict.1053 : U8 = CallByName Dict.22 Dict.544 Dict.1054; + dec Dict.544; + let Dict.549 : U64 = CallByName Num.133 Dict.1053; + let Dict.1052 : U8 = 16i64; + let Dict.1049 : U64 = CallByName Num.72 Dict.547 Dict.1052; + let Dict.1051 : U8 = 8i64; + let Dict.1050 : U64 = CallByName Num.72 Dict.548 Dict.1051; + let Dict.550 : U64 = CallByName Num.71 Dict.1049 Dict.1050; + let Dict.1048 : U64 = CallByName Num.71 Dict.550 Dict.549; + ret Dict.1048; + +procedure Dict.12 (Dict.158): + let Dict.890 : {} = Struct {}; + let Dict.738 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.1 Dict.890; + let Dict.739 : {} = Struct {}; + let Dict.737 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName List.18 Dict.158 Dict.738 Dict.739; + ret Dict.737; + +procedure Dict.127 (Dict.128, Dict.126): + let Dict.1105 : {} = Struct {}; + let Dict.1106 : {} = Struct {}; + let Dict.1107 : {} = Struct {}; + let Dict.1104 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = CallByName Inspect.42 Dict.126 Dict.1105 Dict.1106 Dict.1107; + let Dict.1103 : Str = CallByName Inspect.31 Dict.1104 Dict.128; + ret Dict.1103; + +procedure Dict.159 (Dict.160, Dict.740): + let Dict.161 : Str = StructAtIndex 0 Dict.740; + let Dict.162 : I64 = StructAtIndex 1 Dict.740; + let Dict.741 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.8 Dict.160 Dict.161 Dict.162; + ret Dict.741; + +procedure Dict.188 (Dict.189, Dict.1110, Dict.187): + let Dict.190 : Str = StructAtIndex 0 Dict.1110; + let Dict.191 : I64 = StructAtIndex 1 Dict.1110; + let Dict.1112 : {Str, Int1} = CallByName Inspect.191 Dict.189 Dict.190 Dict.191 Dict.187; + ret Dict.1112; + +procedure Dict.20 (Dict.727): + let Dict.155 : U64 = StructAtIndex 2 Dict.727; + let #Derived_gen.70 : List {U32, U32} = StructAtIndex 0 Dict.727; dec #Derived_gen.70; - let #Derived_gen.69 : List {Str, I64} = StructAtIndex 1 Dict.720; + let #Derived_gen.69 : List {Str, I64} = StructAtIndex 1 Dict.727; dec #Derived_gen.69; - ret Dict.148; + ret Dict.155; procedure Dict.22 (#Attr.2, #Attr.3): - let Dict.765 : {U32, U32} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret Dict.765; + let Dict.772 : {U32, U32} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret Dict.772; procedure Dict.22 (#Attr.2, #Attr.3): - let Dict.781 : {Str, I64} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret Dict.781; + let Dict.788 : {Str, I64} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret Dict.788; procedure Dict.22 (#Attr.2, #Attr.3): - let Dict.944 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret Dict.944; + let Dict.951 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret Dict.951; procedure Dict.23 (#Attr.2): - let Dict.817 : U64 = lowlevel DictPseudoSeed #Attr.2; - ret Dict.817; - -procedure Dict.36 (Dict.119): - let Dict.1093 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Inspect.30 Dict.119; - ret Dict.1093; - -procedure Dict.38 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5, #Derived_gen.6, #Derived_gen.7, #Derived_gen.8): - joinpoint Dict.736 Dict.221 Dict.222 Dict.223 Dict.224 Dict.225 Dict.226 Dict.227 Dict.228 Dict.229: - let Dict.230 : {U32, U32} = CallByName Dict.22 Dict.221 Dict.223; - let Dict.783 : U32 = StructAtIndex 1 Dict.230; - let Dict.771 : Int1 = CallByName Bool.11 Dict.224 Dict.783; - if Dict.771 then - let Dict.782 : U32 = StructAtIndex 0 Dict.230; - let Dict.780 : U64 = CallByName Num.133 Dict.782; - let Dict.779 : {Str, I64} = CallByName Dict.22 Dict.222 Dict.780; - let Dict.231 : Str = StructAtIndex 0 Dict.779; - let Dict.774 : Int1 = CallByName Bool.11 Dict.231 Dict.225; - if Dict.774 then - let Dict.778 : U32 = StructAtIndex 0 Dict.230; - let Dict.776 : U64 = CallByName Num.133 Dict.778; - let Dict.777 : {Str, I64} = Struct {Dict.225, Dict.226}; - let Dict.232 : List {Str, I64} = CallByName List.3 Dict.222 Dict.776 Dict.777; - let Dict.775 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.221, Dict.232, Dict.227, Dict.228, Dict.229}; - ret Dict.775; + let Dict.824 : U64 = lowlevel DictPseudoSeed #Attr.2; + ret Dict.824; + +procedure Dict.4 (Dict.736): + let Dict.163 : List {Str, I64} = StructAtIndex 1 Dict.736; + let #Derived_gen.66 : List {U32, U32} = StructAtIndex 0 Dict.736; + dec #Derived_gen.66; + let Dict.889 : U64 = CallByName List.6 Dict.163; + dec Dict.163; + ret Dict.889; + +procedure Dict.405 (Dict.406, Dict.847, Dict.408, Dict.404): + let Dict.407 : Str = StructAtIndex 0 Dict.847; + inc Dict.406; + let Dict.852 : {U64, U32} = CallByName Dict.72 Dict.406 Dict.407 Dict.404; + let Dict.409 : U64 = StructAtIndex 0 Dict.852; + let Dict.410 : U32 = StructAtIndex 1 Dict.852; + let Dict.851 : U32 = CallByName Num.131 Dict.408; + let Dict.850 : {U32, U32} = Struct {Dict.851, Dict.410}; + let Dict.849 : List {U32, U32} = CallByName Dict.74 Dict.406 Dict.850 Dict.409; + ret Dict.849; + +procedure Dict.43 (Dict.126): + let Dict.1100 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Inspect.30 Dict.126; + ret Dict.1100; + +procedure Dict.45 (#Derived_gen.42, #Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46, #Derived_gen.47, #Derived_gen.48, #Derived_gen.49, #Derived_gen.50): + joinpoint Dict.743 Dict.228 Dict.229 Dict.230 Dict.231 Dict.232 Dict.233 Dict.234 Dict.235 Dict.236: + let Dict.237 : {U32, U32} = CallByName Dict.22 Dict.228 Dict.230; + let Dict.790 : U32 = StructAtIndex 1 Dict.237; + let Dict.778 : Int1 = CallByName Bool.11 Dict.231 Dict.790; + if Dict.778 then + let Dict.789 : U32 = StructAtIndex 0 Dict.237; + let Dict.787 : U64 = CallByName Num.133 Dict.789; + let Dict.786 : {Str, I64} = CallByName Dict.22 Dict.229 Dict.787; + let Dict.238 : Str = StructAtIndex 0 Dict.786; + let Dict.781 : Int1 = CallByName Bool.11 Dict.238 Dict.232; + if Dict.781 then + let Dict.785 : U32 = StructAtIndex 0 Dict.237; + let Dict.783 : U64 = CallByName Num.133 Dict.785; + let Dict.784 : {Str, I64} = Struct {Dict.232, Dict.233}; + let Dict.239 : List {Str, I64} = CallByName List.3 Dict.229 Dict.783 Dict.784; + let Dict.782 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.228, Dict.239, Dict.234, Dict.235, Dict.236}; + ret Dict.782; else - let Dict.773 : U64 = CallByName List.6 Dict.221; - let Dict.233 : U64 = CallByName Dict.68 Dict.223 Dict.773; - let Dict.234 : U32 = CallByName Dict.48 Dict.224; - jump Dict.736 Dict.221 Dict.222 Dict.233 Dict.234 Dict.225 Dict.226 Dict.227 Dict.228 Dict.229; + let Dict.780 : U64 = CallByName List.6 Dict.228; + let Dict.240 : U64 = CallByName Dict.75 Dict.230 Dict.780; + let Dict.241 : U32 = CallByName Dict.55 Dict.231; + jump Dict.743 Dict.228 Dict.229 Dict.240 Dict.241 Dict.232 Dict.233 Dict.234 Dict.235 Dict.236; else - let Dict.770 : U32 = StructAtIndex 1 Dict.230; - let Dict.750 : Int1 = CallByName Num.24 Dict.224 Dict.770; - if Dict.750 then - let Dict.769 : {Str, I64} = Struct {Dict.225, Dict.226}; - let Dict.235 : List {Str, I64} = CallByName List.4 Dict.222 Dict.769; - let Dict.767 : U64 = CallByName List.6 Dict.235; - let Dict.768 : U64 = 1i64; - let Dict.236 : U64 = CallByName Num.75 Dict.767 Dict.768; - let Dict.766 : U32 = CallByName Num.131 Dict.236; - let Dict.752 : {U32, U32} = Struct {Dict.766, Dict.224}; - let Dict.237 : List {U32, U32} = CallByName Dict.67 Dict.221 Dict.752 Dict.223; - let Dict.751 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.237, Dict.235, Dict.227, Dict.228, Dict.229}; - ret Dict.751; + let Dict.777 : U32 = StructAtIndex 1 Dict.237; + let Dict.757 : Int1 = CallByName Num.24 Dict.231 Dict.777; + if Dict.757 then + let Dict.776 : {Str, I64} = Struct {Dict.232, Dict.233}; + let Dict.242 : List {Str, I64} = CallByName List.4 Dict.229 Dict.776; + let Dict.774 : U64 = CallByName List.6 Dict.242; + let Dict.775 : U64 = 1i64; + let Dict.243 : U64 = CallByName Num.75 Dict.774 Dict.775; + let Dict.773 : U32 = CallByName Num.131 Dict.243; + let Dict.759 : {U32, U32} = Struct {Dict.773, Dict.231}; + let Dict.244 : List {U32, U32} = CallByName Dict.74 Dict.228 Dict.759 Dict.230; + let Dict.758 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.244, Dict.242, Dict.234, Dict.235, Dict.236}; + ret Dict.758; else - let Dict.743 : U64 = CallByName List.6 Dict.221; - let Dict.238 : U64 = CallByName Dict.68 Dict.223 Dict.743; - let Dict.239 : U32 = CallByName Dict.48 Dict.224; - jump Dict.736 Dict.221 Dict.222 Dict.238 Dict.239 Dict.225 Dict.226 Dict.227 Dict.228 Dict.229; + let Dict.750 : U64 = CallByName List.6 Dict.228; + let Dict.245 : U64 = CallByName Dict.75 Dict.230 Dict.750; + let Dict.246 : U32 = CallByName Dict.55 Dict.231; + jump Dict.743 Dict.228 Dict.229 Dict.245 Dict.246 Dict.232 Dict.233 Dict.234 Dict.235 Dict.236; in - jump Dict.736 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8; - -procedure Dict.398 (Dict.399, Dict.840, Dict.401, Dict.397): - let Dict.400 : Str = StructAtIndex 0 Dict.840; - inc Dict.399; - let Dict.845 : {U64, U32} = CallByName Dict.65 Dict.399 Dict.400 Dict.397; - let Dict.402 : U64 = StructAtIndex 0 Dict.845; - let Dict.403 : U32 = StructAtIndex 1 Dict.845; - let Dict.844 : U32 = CallByName Num.131 Dict.401; - let Dict.843 : {U32, U32} = Struct {Dict.844, Dict.403}; - let Dict.842 : List {U32, U32} = CallByName Dict.67 Dict.399 Dict.843 Dict.402; - ret Dict.842; - -procedure Dict.4 (Dict.729): - let Dict.156 : List {Str, I64} = StructAtIndex 1 Dict.729; - let #Derived_gen.66 : List {U32, U32} = StructAtIndex 0 Dict.729; - dec #Derived_gen.66; - let Dict.882 : U64 = CallByName List.6 Dict.156; - dec Dict.156; - ret Dict.882; - -procedure Dict.41 (): - let Dict.860 : U32 = 0i64; - let Dict.861 : U32 = 0i64; - let Dict.859 : {U32, U32} = Struct {Dict.860, Dict.861}; - ret Dict.859; - -procedure Dict.42 (): - let Dict.741 : U32 = 1i64; - let Dict.742 : U8 = 8i64; - let Dict.740 : U32 = CallByName Num.72 Dict.741 Dict.742; - ret Dict.740; - -procedure Dict.43 (): - let Dict.791 : U32 = CallByName Dict.42; - let Dict.792 : U32 = 1i64; - let Dict.790 : U32 = CallByName Num.75 Dict.791 Dict.792; - ret Dict.790; - -procedure Dict.44 (): - let Dict.891 : Float32 = 0.8f64; - ret Dict.891; + jump Dict.743 #Derived_gen.42 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46 #Derived_gen.47 #Derived_gen.48 #Derived_gen.49 #Derived_gen.50; + +procedure Dict.48 (): + let Dict.867 : U32 = 0i64; + let Dict.868 : U32 = 0i64; + let Dict.866 : {U32, U32} = Struct {Dict.867, Dict.868}; + ret Dict.866; + +procedure Dict.49 (): + let Dict.748 : U32 = 1i64; + let Dict.749 : U8 = 8i64; + let Dict.747 : U32 = CallByName Num.72 Dict.748 Dict.749; + ret Dict.747; + +procedure Dict.50 (): + let Dict.798 : U32 = CallByName Dict.49; + let Dict.799 : U32 = 1i64; + let Dict.797 : U32 = CallByName Num.75 Dict.798 Dict.799; + ret Dict.797; + +procedure Dict.51 (): + let Dict.898 : Float32 = 0.8f64; + ret Dict.898; + +procedure Dict.52 (): + let Dict.896 : U8 = 64i64; + let Dict.897 : U8 = 3i64; + let Dict.895 : U8 = CallByName Num.75 Dict.896 Dict.897; + ret Dict.895; -procedure Dict.45 (): - let Dict.889 : U8 = 64i64; - let Dict.890 : U8 = 3i64; - let Dict.888 : U8 = CallByName Num.75 Dict.889 Dict.890; - ret Dict.888; - -procedure Dict.46 (): - let Dict.834 : U64 = 1i64; - let Dict.835 : U8 = 32i64; - let Dict.833 : U64 = CallByName Num.72 Dict.834 Dict.835; - ret Dict.833; - -procedure Dict.47 (): - let Dict.832 : U64 = CallByName Dict.46; - ret Dict.832; - -procedure Dict.48 (Dict.306): - let Dict.739 : U32 = CallByName Dict.42; - let Dict.738 : U32 = CallByName Num.51 Dict.306 Dict.739; - ret Dict.738; - -procedure Dict.59 (Dict.719): - let Dict.376 : List {Str, I64} = StructAtIndex 1 Dict.719; - let Dict.377 : U64 = StructAtIndex 2 Dict.719; - let Dict.378 : Float32 = StructAtIndex 3 Dict.719; - let Dict.379 : U8 = StructAtIndex 4 Dict.719; - let #Derived_gen.67 : List {U32, U32} = StructAtIndex 0 Dict.719; - dec #Derived_gen.67; - let Dict.877 : U64 = CallByName Dict.47; - let Dict.836 : Int1 = CallByName Bool.7 Dict.377 Dict.877; - if Dict.836 then - inc Dict.376; - let Dict.876 : U8 = 1i64; - let Dict.380 : U8 = CallByName Num.75 Dict.379 Dict.876; - let Dict.855 : {List {U32, U32}, U64} = CallByName Dict.60 Dict.380 Dict.378; - let Dict.381 : List {U32, U32} = StructAtIndex 0 Dict.855; - let Dict.382 : U64 = StructAtIndex 1 Dict.855; - let Dict.383 : List {U32, U32} = CallByName Dict.64 Dict.381 Dict.376 Dict.380; - let Dict.837 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.383, Dict.376, Dict.382, Dict.378, Dict.380}; - ret Dict.837; +procedure Dict.53 (): + let Dict.841 : U64 = 1i64; + let Dict.842 : U8 = 32i64; + let Dict.840 : U64 = CallByName Num.72 Dict.841 Dict.842; + ret Dict.840; + +procedure Dict.54 (): + let Dict.839 : U64 = CallByName Dict.53; + ret Dict.839; + +procedure Dict.55 (Dict.313): + let Dict.746 : U32 = CallByName Dict.49; + let Dict.745 : U32 = CallByName Num.51 Dict.313 Dict.746; + ret Dict.745; + +procedure Dict.66 (Dict.726): + let Dict.383 : List {Str, I64} = StructAtIndex 1 Dict.726; + let Dict.384 : U64 = StructAtIndex 2 Dict.726; + let Dict.385 : Float32 = StructAtIndex 3 Dict.726; + let Dict.386 : U8 = StructAtIndex 4 Dict.726; + let #Derived_gen.68 : List {U32, U32} = StructAtIndex 0 Dict.726; + dec #Derived_gen.68; + let Dict.884 : U64 = CallByName Dict.54; + let Dict.843 : Int1 = CallByName Bool.7 Dict.384 Dict.884; + if Dict.843 then + inc Dict.383; + let Dict.883 : U8 = 1i64; + let Dict.387 : U8 = CallByName Num.75 Dict.386 Dict.883; + let Dict.862 : {List {U32, U32}, U64} = CallByName Dict.67 Dict.387 Dict.385; + let Dict.388 : List {U32, U32} = StructAtIndex 0 Dict.862; + let Dict.389 : U64 = StructAtIndex 1 Dict.862; + let Dict.390 : List {U32, U32} = CallByName Dict.71 Dict.388 Dict.383 Dict.387; + let Dict.844 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = Struct {Dict.390, Dict.383, Dict.389, Dict.385, Dict.387}; + ret Dict.844; else - dec Dict.376; - let Dict.827 : Str = "Dict hit limit of "; - let Dict.831 : U64 = CallByName Dict.47; - let Dict.829 : Str = CallByName Num.96 Dict.831; - let Dict.830 : Str = " elements. Unable to grow more."; - let Dict.828 : Str = CallByName Str.3 Dict.829 Dict.830; - dec Dict.830; - let Dict.826 : Str = CallByName Str.3 Dict.827 Dict.828; - dec Dict.828; - Crash Dict.826 - -procedure Dict.60 (Dict.384, Dict.385): - let Dict.386 : U64 = CallByName Dict.63 Dict.384; - let Dict.869 : U64 = CallByName Dict.47; - let Dict.864 : Int1 = CallByName Bool.11 Dict.386 Dict.869; - if Dict.864 then - let Dict.867 : {U32, U32} = CallByName Dict.41; - let Dict.868 : U64 = CallByName Dict.47; - let Dict.866 : List {U32, U32} = CallByName List.11 Dict.867 Dict.868; - let Dict.47 : U64 = CallByName Dict.47; - let Dict.865 : {List {U32, U32}, U64} = Struct {Dict.866, Dict.47}; - ret Dict.865; + dec Dict.383; + let Dict.834 : Str = "Dict hit limit of "; + let Dict.838 : U64 = CallByName Dict.54; + let Dict.836 : Str = CallByName Num.96 Dict.838; + let Dict.837 : Str = " elements. Unable to grow more."; + let Dict.835 : Str = CallByName Str.3 Dict.836 Dict.837; + dec Dict.837; + let Dict.833 : Str = CallByName Str.3 Dict.834 Dict.835; + dec Dict.835; + Crash Dict.833 + +procedure Dict.67 (Dict.391, Dict.392): + let Dict.393 : U64 = CallByName Dict.70 Dict.391; + let Dict.876 : U64 = CallByName Dict.54; + let Dict.871 : Int1 = CallByName Bool.11 Dict.393 Dict.876; + if Dict.871 then + let Dict.874 : {U32, U32} = CallByName Dict.48; + let Dict.875 : U64 = CallByName Dict.54; + let Dict.873 : List {U32, U32} = CallByName List.11 Dict.874 Dict.875; + let Dict.54 : U64 = CallByName Dict.54; + let Dict.872 : {List {U32, U32}, U64} = Struct {Dict.873, Dict.54}; + ret Dict.872; else - let Dict.863 : Float32 = CallByName Num.139 Dict.386; - let Dict.862 : Float32 = CallByName Num.21 Dict.863 Dict.385; - let Dict.387 : U64 = CallByName Num.50 Dict.862; - let Dict.858 : {U32, U32} = CallByName Dict.41; - let Dict.857 : List {U32, U32} = CallByName List.11 Dict.858 Dict.386; - let Dict.856 : {List {U32, U32}, U64} = Struct {Dict.857, Dict.387}; - ret Dict.856; - -procedure Dict.63 (Dict.394): - let Dict.873 : U64 = 1i64; - let Dict.875 : U8 = 64i64; - let Dict.874 : U8 = CallByName Num.75 Dict.875 Dict.394; - let Dict.871 : U64 = CallByName Num.72 Dict.873 Dict.874; - let Dict.872 : U64 = CallByName Dict.47; - let Dict.870 : U64 = CallByName Num.148 Dict.871 Dict.872; - ret Dict.870; - -procedure Dict.64 (Dict.395, Dict.396, Dict.397): - let Dict.838 : List {U32, U32} = CallByName List.83 Dict.396 Dict.395 Dict.397; - ret Dict.838; - -procedure Dict.65 (Dict.404, Dict.405, Dict.406): - let Dict.407 : U64 = CallByName Dict.69 Dict.405; - let Dict.408 : U32 = CallByName Dict.70 Dict.407; - let Dict.409 : U64 = CallByName Dict.71 Dict.407 Dict.406; - let Dict.846 : {U64, U32} = CallByName Dict.66 Dict.404 Dict.409 Dict.408; - ret Dict.846; - -procedure Dict.66 (#Derived_gen.40, #Derived_gen.41, #Derived_gen.42): - joinpoint Dict.847 Dict.410 Dict.411 Dict.412: - let Dict.413 : {U32, U32} = CallByName Dict.22 Dict.410 Dict.411; - let Dict.854 : U32 = StructAtIndex 1 Dict.413; - let Dict.849 : Int1 = CallByName Num.22 Dict.412 Dict.854; - if Dict.849 then - let Dict.853 : U64 = CallByName List.6 Dict.410; - let Dict.851 : U64 = CallByName Dict.68 Dict.411 Dict.853; - let Dict.852 : U32 = CallByName Dict.48 Dict.412; - jump Dict.847 Dict.410 Dict.851 Dict.852; + let Dict.870 : Float32 = CallByName Num.139 Dict.393; + let Dict.869 : Float32 = CallByName Num.21 Dict.870 Dict.392; + let Dict.394 : U64 = CallByName Num.50 Dict.869; + let Dict.865 : {U32, U32} = CallByName Dict.48; + let Dict.864 : List {U32, U32} = CallByName List.11 Dict.865 Dict.393; + let Dict.863 : {List {U32, U32}, U64} = Struct {Dict.864, Dict.394}; + ret Dict.863; + +procedure Dict.70 (Dict.401): + let Dict.880 : U64 = 1i64; + let Dict.882 : U8 = 64i64; + let Dict.881 : U8 = CallByName Num.75 Dict.882 Dict.401; + let Dict.878 : U64 = CallByName Num.72 Dict.880 Dict.881; + let Dict.879 : U64 = CallByName Dict.54; + let Dict.877 : U64 = CallByName Num.148 Dict.878 Dict.879; + ret Dict.877; + +procedure Dict.71 (Dict.402, Dict.403, Dict.404): + let Dict.845 : List {U32, U32} = CallByName List.83 Dict.403 Dict.402 Dict.404; + ret Dict.845; + +procedure Dict.72 (Dict.411, Dict.412, Dict.413): + let Dict.414 : U64 = CallByName Dict.76 Dict.412; + let Dict.415 : U32 = CallByName Dict.77 Dict.414; + let Dict.416 : U64 = CallByName Dict.78 Dict.414 Dict.413; + let Dict.853 : {U64, U32} = CallByName Dict.73 Dict.411 Dict.416 Dict.415; + ret Dict.853; + +procedure Dict.73 (#Derived_gen.16, #Derived_gen.17, #Derived_gen.18): + joinpoint Dict.854 Dict.417 Dict.418 Dict.419: + let Dict.420 : {U32, U32} = CallByName Dict.22 Dict.417 Dict.418; + let Dict.861 : U32 = StructAtIndex 1 Dict.420; + let Dict.856 : Int1 = CallByName Num.22 Dict.419 Dict.861; + if Dict.856 then + let Dict.860 : U64 = CallByName List.6 Dict.417; + let Dict.858 : U64 = CallByName Dict.75 Dict.418 Dict.860; + let Dict.859 : U32 = CallByName Dict.55 Dict.419; + jump Dict.854 Dict.417 Dict.858 Dict.859; else - dec Dict.410; - let Dict.848 : {U64, U32} = Struct {Dict.411, Dict.412}; - ret Dict.848; + dec Dict.417; + let Dict.855 : {U64, U32} = Struct {Dict.418, Dict.419}; + ret Dict.855; in - jump Dict.847 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42; - -procedure Dict.67 (#Derived_gen.28, #Derived_gen.29, #Derived_gen.30): - joinpoint Dict.753 Dict.414 Dict.415 Dict.416: - let Dict.417 : {U32, U32} = CallByName Dict.22 Dict.414 Dict.416; - let Dict.763 : U32 = StructAtIndex 1 Dict.417; - let Dict.764 : U32 = 0i64; - let Dict.755 : Int1 = CallByName Bool.7 Dict.763 Dict.764; - if Dict.755 then - let Dict.418 : List {U32, U32} = CallByName List.3 Dict.414 Dict.416 Dict.415; - let Dict.760 : U32 = StructAtIndex 0 Dict.417; - let Dict.761 : U32 = StructAtIndex 1 Dict.417; - let Dict.762 : U32 = CallByName Dict.48 Dict.761; - let Dict.757 : {U32, U32} = Struct {Dict.760, Dict.762}; - let Dict.759 : U64 = CallByName List.6 Dict.418; - let Dict.758 : U64 = CallByName Dict.68 Dict.416 Dict.759; - jump Dict.753 Dict.418 Dict.757 Dict.758; + jump Dict.854 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; + +procedure Dict.74 (#Derived_gen.51, #Derived_gen.52, #Derived_gen.53): + joinpoint Dict.760 Dict.421 Dict.422 Dict.423: + let Dict.424 : {U32, U32} = CallByName Dict.22 Dict.421 Dict.423; + let Dict.770 : U32 = StructAtIndex 1 Dict.424; + let Dict.771 : U32 = 0i64; + let Dict.762 : Int1 = CallByName Bool.7 Dict.770 Dict.771; + if Dict.762 then + let Dict.425 : List {U32, U32} = CallByName List.3 Dict.421 Dict.423 Dict.422; + let Dict.767 : U32 = StructAtIndex 0 Dict.424; + let Dict.768 : U32 = StructAtIndex 1 Dict.424; + let Dict.769 : U32 = CallByName Dict.55 Dict.768; + let Dict.764 : {U32, U32} = Struct {Dict.767, Dict.769}; + let Dict.766 : U64 = CallByName List.6 Dict.425; + let Dict.765 : U64 = CallByName Dict.75 Dict.423 Dict.766; + jump Dict.760 Dict.425 Dict.764 Dict.765; else - let Dict.754 : List {U32, U32} = CallByName List.3 Dict.414 Dict.416 Dict.415; - ret Dict.754; - in - jump Dict.753 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; - -procedure Dict.68 (Dict.419, Dict.420): - let Dict.749 : U64 = 1i64; - let Dict.748 : U64 = CallByName Num.51 Dict.419 Dict.749; - let Dict.745 : Int1 = CallByName Bool.7 Dict.748 Dict.420; - if Dict.745 then - let Dict.747 : U64 = 1i64; - let Dict.746 : U64 = CallByName Num.51 Dict.419 Dict.747; - ret Dict.746; - else - let Dict.744 : U64 = 0i64; - ret Dict.744; - -procedure Dict.69 (Dict.421): - let Dict.797 : [C , C U64] = TagId(0) ; - let Dict.796 : {U64, U64} = CallByName Dict.73 Dict.797; - let Dict.794 : {U64, U64} = CallByName Hash.19 Dict.796 Dict.421; - let Dict.793 : U64 = CallByName Dict.76 Dict.794; - ret Dict.793; - -procedure Dict.70 (Dict.423): - let Dict.788 : U32 = CallByName Num.131 Dict.423; - let Dict.789 : U32 = CallByName Dict.43; - let Dict.786 : U32 = CallByName Num.69 Dict.788 Dict.789; - let Dict.787 : U32 = CallByName Dict.42; - let Dict.785 : U32 = CallByName Num.71 Dict.786 Dict.787; - ret Dict.785; - -procedure Dict.71 (Dict.424, Dict.425): - let Dict.784 : U64 = CallByName Num.74 Dict.424 Dict.425; - ret Dict.784; - -procedure Dict.73 (Dict.427): - joinpoint Dict.814 Dict.428: - let Dict.799 : U64 = CallByName Dict.75 Dict.428; - let Dict.798 : {U64, U64} = Struct {Dict.799, Dict.428}; - ret Dict.798; + let Dict.761 : List {U32, U32} = CallByName List.3 Dict.421 Dict.423 Dict.422; + ret Dict.761; in - let Dict.819 : U8 = 0i64; - let Dict.820 : U8 = GetTagId Dict.427; - let Dict.821 : Int1 = lowlevel Eq Dict.819 Dict.820; - if Dict.821 then - let Dict.816 : {} = Struct {}; - let Dict.815 : U64 = CallByName Dict.23 Dict.816; - jump Dict.814 Dict.815; + jump Dict.760 #Derived_gen.51 #Derived_gen.52 #Derived_gen.53; + +procedure Dict.75 (Dict.426, Dict.427): + let Dict.756 : U64 = 1i64; + let Dict.755 : U64 = CallByName Num.51 Dict.426 Dict.756; + let Dict.752 : Int1 = CallByName Bool.7 Dict.755 Dict.427; + if Dict.752 then + let Dict.754 : U64 = 1i64; + let Dict.753 : U64 = CallByName Num.51 Dict.426 Dict.754; + ret Dict.753; else - let Dict.429 : U64 = UnionAtIndex (Id 1) (Index 0) Dict.427; - jump Dict.814 Dict.429; - -procedure Dict.74 (Dict.708, Dict.709): - let Dict.432 : U64 = StructAtIndex 0 Dict.709; - let Dict.433 : U64 = StructAtIndex 1 Dict.709; - let Dict.435 : U64 = StructAtIndex 2 Dict.709; - let Dict.434 : U64 = StructAtIndex 3 Dict.709; - let Dict.430 : U64 = StructAtIndex 0 Dict.708; - let Dict.431 : U64 = StructAtIndex 1 Dict.708; - let Dict.913 : U64 = CallByName Dict.86; - let Dict.911 : U64 = CallByName Num.70 Dict.432 Dict.913; - let Dict.912 : U64 = CallByName Num.70 Dict.433 Dict.434; - let Dict.436 : {U64, U64} = CallByName Dict.90 Dict.911 Dict.912; - let Dict.908 : U64 = StructAtIndex 0 Dict.436; - let Dict.909 : U64 = CallByName Dict.85; - let Dict.907 : U64 = CallByName Num.70 Dict.908 Dict.909; - let Dict.437 : U64 = CallByName Num.70 Dict.907 Dict.435; - let Dict.904 : U64 = StructAtIndex 1 Dict.436; - let Dict.905 : U64 = CallByName Dict.86; - let Dict.438 : U64 = CallByName Num.70 Dict.904 Dict.905; - let Dict.439 : U64 = CallByName Dict.89 Dict.437 Dict.438; - let Dict.896 : U64 = CallByName Dict.89 Dict.431 Dict.439; - let Dict.895 : {U64, U64} = Struct {Dict.430, Dict.896}; - ret Dict.895; - -procedure Dict.75 (Dict.440): - let Dict.812 : U64 = CallByName Dict.85; - let Dict.802 : U64 = CallByName Num.70 Dict.440 Dict.812; - let Dict.803 : U64 = CallByName Dict.86; - let Dict.801 : U64 = CallByName Dict.89 Dict.802 Dict.803; - let Dict.800 : U64 = CallByName Num.70 Dict.801 Dict.440; + let Dict.751 : U64 = 0i64; + ret Dict.751; + +procedure Dict.76 (Dict.428): + let Dict.804 : [C , C U64] = TagId(0) ; + let Dict.803 : {U64, U64} = CallByName Dict.80 Dict.804; + let Dict.801 : {U64, U64} = CallByName Hash.19 Dict.803 Dict.428; + let Dict.800 : U64 = CallByName Dict.83 Dict.801; ret Dict.800; -procedure Dict.76 (Dict.727): - let Dict.441 : U64 = StructAtIndex 1 Dict.727; - ret Dict.441; - -procedure Dict.8 (Dict.210, Dict.211, Dict.212): - joinpoint Dict.824 Dict.822: - let Dict.213 : List {U32, U32} = StructAtIndex 0 Dict.822; - let Dict.214 : List {Str, I64} = StructAtIndex 1 Dict.822; - let Dict.215 : U64 = StructAtIndex 2 Dict.822; - let Dict.216 : Float32 = StructAtIndex 3 Dict.822; - let Dict.217 : U8 = StructAtIndex 4 Dict.822; - inc Dict.211; - let Dict.218 : U64 = CallByName Dict.69 Dict.211; - let Dict.219 : U32 = CallByName Dict.70 Dict.218; - let Dict.220 : U64 = CallByName Dict.71 Dict.218 Dict.217; - let Dict.735 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.38 Dict.213 Dict.214 Dict.220 Dict.219 Dict.211 Dict.212 Dict.215 Dict.216 Dict.217; - ret Dict.735; +procedure Dict.77 (Dict.430): + let Dict.795 : U32 = CallByName Num.131 Dict.430; + let Dict.796 : U32 = CallByName Dict.50; + let Dict.793 : U32 = CallByName Num.69 Dict.795 Dict.796; + let Dict.794 : U32 = CallByName Dict.49; + let Dict.792 : U32 = CallByName Num.71 Dict.793 Dict.794; + ret Dict.792; + +procedure Dict.78 (Dict.431, Dict.432): + let Dict.791 : U64 = CallByName Num.74 Dict.431 Dict.432; + ret Dict.791; + +procedure Dict.8 (Dict.217, Dict.218, Dict.219): + joinpoint Dict.831 Dict.829: + let Dict.220 : List {U32, U32} = StructAtIndex 0 Dict.829; + let Dict.221 : List {Str, I64} = StructAtIndex 1 Dict.829; + let Dict.222 : U64 = StructAtIndex 2 Dict.829; + let Dict.223 : Float32 = StructAtIndex 3 Dict.829; + let Dict.224 : U8 = StructAtIndex 4 Dict.829; + inc Dict.218; + let Dict.225 : U64 = CallByName Dict.76 Dict.218; + let Dict.226 : U32 = CallByName Dict.77 Dict.225; + let Dict.227 : U64 = CallByName Dict.78 Dict.225 Dict.224; + let Dict.742 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.45 Dict.220 Dict.221 Dict.227 Dict.226 Dict.218 Dict.219 Dict.222 Dict.223 Dict.224; + ret Dict.742; + in + inc 2 Dict.217; + let Dict.886 : U64 = CallByName Dict.4 Dict.217; + let Dict.887 : U64 = CallByName Dict.20 Dict.217; + let Dict.885 : Int1 = CallByName Num.22 Dict.886 Dict.887; + if Dict.885 then + jump Dict.831 Dict.217; + else + let Dict.830 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.66 Dict.217; + jump Dict.831 Dict.830; + +procedure Dict.80 (Dict.434): + joinpoint Dict.821 Dict.435: + let Dict.806 : U64 = CallByName Dict.82 Dict.435; + let Dict.805 : {U64, U64} = Struct {Dict.806, Dict.435}; + ret Dict.805; in - inc 2 Dict.210; - let Dict.879 : U64 = CallByName Dict.4 Dict.210; - let Dict.880 : U64 = CallByName Dict.20 Dict.210; - let Dict.878 : Int1 = CallByName Num.22 Dict.879 Dict.880; - if Dict.878 then - jump Dict.824 Dict.210; + let Dict.826 : U8 = 0i64; + let Dict.827 : U8 = GetTagId Dict.434; + let Dict.828 : Int1 = lowlevel Eq Dict.826 Dict.827; + if Dict.828 then + let Dict.823 : {} = Struct {}; + let Dict.822 : U64 = CallByName Dict.23 Dict.823; + jump Dict.821 Dict.822; else - let Dict.823 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.59 Dict.210; - jump Dict.824 Dict.823; - -procedure Dict.82 (Dict.702, Dict.480): - let Dict.478 : U64 = StructAtIndex 0 Dict.702; - let Dict.479 : U64 = StructAtIndex 1 Dict.702; - let Dict.481 : U64 = CallByName List.6 Dict.480; - joinpoint Dict.918 Dict.482: - let Dict.893 : {U64, U64} = Struct {Dict.478, Dict.479}; - let Dict.914 : U64 = StructAtIndex 0 Dict.482; - let Dict.915 : U64 = StructAtIndex 1 Dict.482; - let Dict.916 : U64 = StructAtIndex 2 Dict.482; - let Dict.894 : {U64, U64, U64, U64} = Struct {Dict.914, Dict.915, Dict.481, Dict.916}; - let Dict.892 : {U64, U64} = CallByName Dict.74 Dict.893 Dict.894; - ret Dict.892; + let Dict.436 : U64 = UnionAtIndex (Id 1) (Index 0) Dict.434; + jump Dict.821 Dict.436; + +procedure Dict.81 (Dict.715, Dict.716): + let Dict.439 : U64 = StructAtIndex 0 Dict.716; + let Dict.440 : U64 = StructAtIndex 1 Dict.716; + let Dict.442 : U64 = StructAtIndex 2 Dict.716; + let Dict.441 : U64 = StructAtIndex 3 Dict.716; + let Dict.437 : U64 = StructAtIndex 0 Dict.715; + let Dict.438 : U64 = StructAtIndex 1 Dict.715; + let Dict.920 : U64 = CallByName Dict.93; + let Dict.918 : U64 = CallByName Num.70 Dict.439 Dict.920; + let Dict.919 : U64 = CallByName Num.70 Dict.440 Dict.441; + let Dict.443 : {U64, U64} = CallByName Dict.97 Dict.918 Dict.919; + let Dict.915 : U64 = StructAtIndex 0 Dict.443; + let Dict.916 : U64 = CallByName Dict.92; + let Dict.914 : U64 = CallByName Num.70 Dict.915 Dict.916; + let Dict.444 : U64 = CallByName Num.70 Dict.914 Dict.442; + let Dict.911 : U64 = StructAtIndex 1 Dict.443; + let Dict.912 : U64 = CallByName Dict.93; + let Dict.445 : U64 = CallByName Num.70 Dict.911 Dict.912; + let Dict.446 : U64 = CallByName Dict.96 Dict.444 Dict.445; + let Dict.903 : U64 = CallByName Dict.96 Dict.438 Dict.446; + let Dict.902 : {U64, U64} = Struct {Dict.437, Dict.903}; + ret Dict.902; + +procedure Dict.82 (Dict.447): + let Dict.819 : U64 = CallByName Dict.92; + let Dict.809 : U64 = CallByName Num.70 Dict.447 Dict.819; + let Dict.810 : U64 = CallByName Dict.93; + let Dict.808 : U64 = CallByName Dict.96 Dict.809 Dict.810; + let Dict.807 : U64 = CallByName Num.70 Dict.808 Dict.447; + ret Dict.807; + +procedure Dict.83 (Dict.734): + let Dict.448 : U64 = StructAtIndex 1 Dict.734; + ret Dict.448; + +procedure Dict.89 (Dict.709, Dict.487): + let Dict.485 : U64 = StructAtIndex 0 Dict.709; + let Dict.486 : U64 = StructAtIndex 1 Dict.709; + let Dict.488 : U64 = CallByName List.6 Dict.487; + joinpoint Dict.925 Dict.489: + let Dict.900 : {U64, U64} = Struct {Dict.485, Dict.486}; + let Dict.921 : U64 = StructAtIndex 0 Dict.489; + let Dict.922 : U64 = StructAtIndex 1 Dict.489; + let Dict.923 : U64 = StructAtIndex 2 Dict.489; + let Dict.901 : {U64, U64, U64, U64} = Struct {Dict.921, Dict.922, Dict.488, Dict.923}; + let Dict.899 : {U64, U64} = CallByName Dict.81 Dict.900 Dict.901; + ret Dict.899; in - let Dict.1092 : U64 = 16i64; - let Dict.1032 : Int1 = CallByName Num.23 Dict.481 Dict.1092; - if Dict.1032 then - joinpoint Dict.1034 Dict.917: - jump Dict.918 Dict.917; + let Dict.1099 : U64 = 16i64; + let Dict.1039 : Int1 = CallByName Num.23 Dict.488 Dict.1099; + if Dict.1039 then + joinpoint Dict.1041 Dict.924: + jump Dict.925 Dict.924; in - let Dict.1091 : U64 = 4i64; - let Dict.1056 : Int1 = CallByName Num.25 Dict.481 Dict.1091; - if Dict.1056 then - let Dict.1090 : U8 = 3i64; - let Dict.1088 : U64 = CallByName Num.74 Dict.481 Dict.1090; - let Dict.1089 : U8 = 2i64; - let Dict.483 : U64 = CallByName Num.72 Dict.1088 Dict.1089; - let Dict.1087 : U64 = 0i64; - inc 3 Dict.480; - let Dict.1085 : U64 = CallByName Dict.92 Dict.480 Dict.1087; - let Dict.1086 : U8 = 32i64; - let Dict.1083 : U64 = CallByName Num.72 Dict.1085 Dict.1086; - let Dict.1084 : U64 = CallByName Dict.92 Dict.480 Dict.483; - let Dict.484 : U64 = CallByName Num.71 Dict.1083 Dict.1084; - let Dict.1082 : U64 = 4i64; - let Dict.1081 : U64 = CallByName Num.75 Dict.481 Dict.1082; - let Dict.1079 : U64 = CallByName Dict.92 Dict.480 Dict.1081; - let Dict.1080 : U8 = 32i64; - let Dict.1057 : U64 = CallByName Num.72 Dict.1079 Dict.1080; - let Dict.1078 : U64 = 4i64; - let Dict.1077 : U64 = CallByName Num.75 Dict.481 Dict.1078; - let Dict.1059 : U64 = CallByName Num.75 Dict.1077 Dict.483; - let Dict.1058 : U64 = CallByName Dict.92 Dict.480 Dict.1059; - let Dict.485 : U64 = CallByName Num.71 Dict.1057 Dict.1058; - let Dict.1033 : {U64, U64, U64} = Struct {Dict.484, Dict.485, Dict.478}; - jump Dict.1034 Dict.1033; + let Dict.1098 : U64 = 4i64; + let Dict.1063 : Int1 = CallByName Num.25 Dict.488 Dict.1098; + if Dict.1063 then + let Dict.1097 : U8 = 3i64; + let Dict.1095 : U64 = CallByName Num.74 Dict.488 Dict.1097; + let Dict.1096 : U8 = 2i64; + let Dict.490 : U64 = CallByName Num.72 Dict.1095 Dict.1096; + let Dict.1094 : U64 = 0i64; + inc 3 Dict.487; + let Dict.1092 : U64 = CallByName Dict.99 Dict.487 Dict.1094; + let Dict.1093 : U8 = 32i64; + let Dict.1090 : U64 = CallByName Num.72 Dict.1092 Dict.1093; + let Dict.1091 : U64 = CallByName Dict.99 Dict.487 Dict.490; + let Dict.491 : U64 = CallByName Num.71 Dict.1090 Dict.1091; + let Dict.1089 : U64 = 4i64; + let Dict.1088 : U64 = CallByName Num.75 Dict.488 Dict.1089; + let Dict.1086 : U64 = CallByName Dict.99 Dict.487 Dict.1088; + let Dict.1087 : U8 = 32i64; + let Dict.1064 : U64 = CallByName Num.72 Dict.1086 Dict.1087; + let Dict.1085 : U64 = 4i64; + let Dict.1084 : U64 = CallByName Num.75 Dict.488 Dict.1085; + let Dict.1066 : U64 = CallByName Num.75 Dict.1084 Dict.490; + let Dict.1065 : U64 = CallByName Dict.99 Dict.487 Dict.1066; + let Dict.492 : U64 = CallByName Num.71 Dict.1064 Dict.1065; + let Dict.1040 : {U64, U64, U64} = Struct {Dict.491, Dict.492, Dict.485}; + jump Dict.1041 Dict.1040; else - let Dict.1055 : U64 = 0i64; - let Dict.1037 : Int1 = CallByName Num.24 Dict.481 Dict.1055; - if Dict.1037 then - let Dict.1040 : U64 = 0i64; - let Dict.1038 : U64 = CallByName Dict.93 Dict.480 Dict.1040 Dict.481; - let Dict.1039 : U64 = 0i64; - let Dict.1033 : {U64, U64, U64} = Struct {Dict.1038, Dict.1039, Dict.478}; - jump Dict.1034 Dict.1033; + let Dict.1062 : U64 = 0i64; + let Dict.1044 : Int1 = CallByName Num.24 Dict.488 Dict.1062; + if Dict.1044 then + let Dict.1047 : U64 = 0i64; + let Dict.1045 : U64 = CallByName Dict.100 Dict.487 Dict.1047 Dict.488; + let Dict.1046 : U64 = 0i64; + let Dict.1040 : {U64, U64, U64} = Struct {Dict.1045, Dict.1046, Dict.485}; + jump Dict.1041 Dict.1040; else - dec Dict.480; - let Dict.1035 : U64 = 0i64; - let Dict.1036 : U64 = 0i64; - let Dict.1033 : {U64, U64, U64} = Struct {Dict.1035, Dict.1036, Dict.478}; - jump Dict.1034 Dict.1033; + dec Dict.487; + let Dict.1042 : U64 = 0i64; + let Dict.1043 : U64 = 0i64; + let Dict.1040 : {U64, U64, U64} = Struct {Dict.1042, Dict.1043, Dict.485}; + jump Dict.1041 Dict.1040; else - let Dict.1031 : U64 = 48i64; - let Dict.1029 : Int1 = CallByName Num.23 Dict.481 Dict.1031; - if Dict.1029 then - let Dict.1030 : U64 = 0i64; - let Dict.917 : {U64, U64, U64} = CallByName Dict.84 Dict.478 Dict.480 Dict.1030 Dict.481; - jump Dict.918 Dict.917; + let Dict.1038 : U64 = 48i64; + let Dict.1036 : Int1 = CallByName Num.23 Dict.488 Dict.1038; + if Dict.1036 then + let Dict.1037 : U64 = 0i64; + let Dict.924 : {U64, U64, U64} = CallByName Dict.91 Dict.485 Dict.487 Dict.1037 Dict.488; + jump Dict.925 Dict.924; else - let Dict.919 : U64 = 0i64; - let Dict.917 : {U64, U64, U64} = CallByName Dict.83 Dict.478 Dict.478 Dict.478 Dict.480 Dict.919 Dict.481; - jump Dict.918 Dict.917; - -procedure Dict.83 (#Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): - joinpoint Dict.920 Dict.486 Dict.487 Dict.488 Dict.489 Dict.490 Dict.491: - inc 6 Dict.489; - let Dict.1027 : U64 = CallByName Dict.91 Dict.489 Dict.490; - let Dict.1028 : U64 = CallByName Dict.86; - let Dict.1022 : U64 = CallByName Num.70 Dict.1027 Dict.1028; - let Dict.1026 : U64 = 8i64; - let Dict.1025 : U64 = CallByName Num.51 Dict.490 Dict.1026; - let Dict.1024 : U64 = CallByName Dict.91 Dict.489 Dict.1025; - let Dict.1023 : U64 = CallByName Num.70 Dict.1024 Dict.486; - let Dict.492 : U64 = CallByName Dict.89 Dict.1022 Dict.1023; - let Dict.1021 : U64 = 16i64; - let Dict.1020 : U64 = CallByName Num.51 Dict.490 Dict.1021; - let Dict.1017 : U64 = CallByName Dict.91 Dict.489 Dict.1020; - let Dict.1018 : U64 = CallByName Dict.87; - let Dict.1012 : U64 = CallByName Num.70 Dict.1017 Dict.1018; - let Dict.1016 : U64 = 24i64; - let Dict.1015 : U64 = CallByName Num.51 Dict.490 Dict.1016; - let Dict.1014 : U64 = CallByName Dict.91 Dict.489 Dict.1015; - let Dict.1013 : U64 = CallByName Num.70 Dict.1014 Dict.487; - let Dict.493 : U64 = CallByName Dict.89 Dict.1012 Dict.1013; - let Dict.1011 : U64 = 32i64; - let Dict.1010 : U64 = CallByName Num.51 Dict.490 Dict.1011; - let Dict.1007 : U64 = CallByName Dict.91 Dict.489 Dict.1010; - let Dict.1008 : U64 = CallByName Dict.88; - let Dict.1002 : U64 = CallByName Num.70 Dict.1007 Dict.1008; - let Dict.1006 : U64 = 40i64; - let Dict.1005 : U64 = CallByName Num.51 Dict.490 Dict.1006; - let Dict.1004 : U64 = CallByName Dict.91 Dict.489 Dict.1005; - let Dict.1003 : U64 = CallByName Num.70 Dict.1004 Dict.488; - let Dict.494 : U64 = CallByName Dict.89 Dict.1002 Dict.1003; - let Dict.1001 : U64 = 48i64; - let Dict.495 : U64 = CallByName Num.75 Dict.491 Dict.1001; - let Dict.1000 : U64 = 48i64; - let Dict.496 : U64 = CallByName Num.51 Dict.490 Dict.1000; - let Dict.999 : U64 = 48i64; - let Dict.997 : Int1 = CallByName Num.24 Dict.495 Dict.999; - if Dict.997 then - jump Dict.920 Dict.492 Dict.493 Dict.494 Dict.489 Dict.496 Dict.495; + let Dict.926 : U64 = 0i64; + let Dict.924 : {U64, U64, U64} = CallByName Dict.90 Dict.485 Dict.485 Dict.485 Dict.487 Dict.926 Dict.488; + jump Dict.925 Dict.924; + +procedure Dict.90 (#Derived_gen.6, #Derived_gen.7, #Derived_gen.8, #Derived_gen.9, #Derived_gen.10, #Derived_gen.11): + joinpoint Dict.927 Dict.493 Dict.494 Dict.495 Dict.496 Dict.497 Dict.498: + inc 6 Dict.496; + let Dict.1034 : U64 = CallByName Dict.98 Dict.496 Dict.497; + let Dict.1035 : U64 = CallByName Dict.93; + let Dict.1029 : U64 = CallByName Num.70 Dict.1034 Dict.1035; + let Dict.1033 : U64 = 8i64; + let Dict.1032 : U64 = CallByName Num.51 Dict.497 Dict.1033; + let Dict.1031 : U64 = CallByName Dict.98 Dict.496 Dict.1032; + let Dict.1030 : U64 = CallByName Num.70 Dict.1031 Dict.493; + let Dict.499 : U64 = CallByName Dict.96 Dict.1029 Dict.1030; + let Dict.1028 : U64 = 16i64; + let Dict.1027 : U64 = CallByName Num.51 Dict.497 Dict.1028; + let Dict.1024 : U64 = CallByName Dict.98 Dict.496 Dict.1027; + let Dict.1025 : U64 = CallByName Dict.94; + let Dict.1019 : U64 = CallByName Num.70 Dict.1024 Dict.1025; + let Dict.1023 : U64 = 24i64; + let Dict.1022 : U64 = CallByName Num.51 Dict.497 Dict.1023; + let Dict.1021 : U64 = CallByName Dict.98 Dict.496 Dict.1022; + let Dict.1020 : U64 = CallByName Num.70 Dict.1021 Dict.494; + let Dict.500 : U64 = CallByName Dict.96 Dict.1019 Dict.1020; + let Dict.1018 : U64 = 32i64; + let Dict.1017 : U64 = CallByName Num.51 Dict.497 Dict.1018; + let Dict.1014 : U64 = CallByName Dict.98 Dict.496 Dict.1017; + let Dict.1015 : U64 = CallByName Dict.95; + let Dict.1009 : U64 = CallByName Num.70 Dict.1014 Dict.1015; + let Dict.1013 : U64 = 40i64; + let Dict.1012 : U64 = CallByName Num.51 Dict.497 Dict.1013; + let Dict.1011 : U64 = CallByName Dict.98 Dict.496 Dict.1012; + let Dict.1010 : U64 = CallByName Num.70 Dict.1011 Dict.495; + let Dict.501 : U64 = CallByName Dict.96 Dict.1009 Dict.1010; + let Dict.1008 : U64 = 48i64; + let Dict.502 : U64 = CallByName Num.75 Dict.498 Dict.1008; + let Dict.1007 : U64 = 48i64; + let Dict.503 : U64 = CallByName Num.51 Dict.497 Dict.1007; + let Dict.1006 : U64 = 48i64; + let Dict.1004 : Int1 = CallByName Num.24 Dict.502 Dict.1006; + if Dict.1004 then + jump Dict.927 Dict.499 Dict.500 Dict.501 Dict.496 Dict.503 Dict.502; else - let Dict.996 : U64 = 16i64; - let Dict.971 : Int1 = CallByName Num.24 Dict.495 Dict.996; - if Dict.971 then - let Dict.995 : U64 = CallByName Num.70 Dict.493 Dict.492; - let Dict.497 : U64 = CallByName Num.70 Dict.494 Dict.995; - let Dict.972 : {U64, U64, U64} = CallByName Dict.84 Dict.497 Dict.489 Dict.496 Dict.495; - ret Dict.972; + let Dict.1003 : U64 = 16i64; + let Dict.978 : Int1 = CallByName Num.24 Dict.502 Dict.1003; + if Dict.978 then + let Dict.1002 : U64 = CallByName Num.70 Dict.500 Dict.499; + let Dict.504 : U64 = CallByName Num.70 Dict.501 Dict.1002; + let Dict.979 : {U64, U64, U64} = CallByName Dict.91 Dict.504 Dict.496 Dict.503 Dict.502; + ret Dict.979; else - inc Dict.489; - let Dict.970 : U64 = CallByName Num.70 Dict.493 Dict.492; - let Dict.498 : U64 = CallByName Num.70 Dict.494 Dict.970; - let Dict.969 : U64 = 16i64; - let Dict.968 : U64 = CallByName Num.75 Dict.495 Dict.969; - let Dict.967 : U64 = CallByName Num.51 Dict.968 Dict.496; - let Dict.922 : U64 = CallByName Dict.91 Dict.489 Dict.967; - let Dict.966 : U64 = 8i64; - let Dict.965 : U64 = CallByName Num.75 Dict.495 Dict.966; - let Dict.924 : U64 = CallByName Num.51 Dict.965 Dict.496; - let Dict.923 : U64 = CallByName Dict.91 Dict.489 Dict.924; - let Dict.921 : {U64, U64, U64} = Struct {Dict.922, Dict.923, Dict.498}; - ret Dict.921; + inc Dict.496; + let Dict.977 : U64 = CallByName Num.70 Dict.500 Dict.499; + let Dict.505 : U64 = CallByName Num.70 Dict.501 Dict.977; + let Dict.976 : U64 = 16i64; + let Dict.975 : U64 = CallByName Num.75 Dict.502 Dict.976; + let Dict.974 : U64 = CallByName Num.51 Dict.975 Dict.503; + let Dict.929 : U64 = CallByName Dict.98 Dict.496 Dict.974; + let Dict.973 : U64 = 8i64; + let Dict.972 : U64 = CallByName Num.75 Dict.502 Dict.973; + let Dict.931 : U64 = CallByName Num.51 Dict.972 Dict.503; + let Dict.930 : U64 = CallByName Dict.98 Dict.496 Dict.931; + let Dict.928 : {U64, U64, U64} = Struct {Dict.929, Dict.930, Dict.505}; + ret Dict.928; in - jump Dict.920 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14; - -procedure Dict.84 (#Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46): - joinpoint Dict.973 Dict.499 Dict.500 Dict.501 Dict.502: - inc 2 Dict.500; - let Dict.993 : U64 = CallByName Dict.91 Dict.500 Dict.501; - let Dict.994 : U64 = CallByName Dict.86; - let Dict.988 : U64 = CallByName Num.70 Dict.993 Dict.994; - let Dict.992 : U64 = 8i64; - let Dict.991 : U64 = CallByName Num.51 Dict.501 Dict.992; - let Dict.990 : U64 = CallByName Dict.91 Dict.500 Dict.991; - let Dict.989 : U64 = CallByName Num.70 Dict.990 Dict.499; - let Dict.503 : U64 = CallByName Dict.89 Dict.988 Dict.989; - let Dict.987 : U64 = 16i64; - let Dict.504 : U64 = CallByName Num.75 Dict.502 Dict.987; - let Dict.986 : U64 = 16i64; - let Dict.505 : U64 = CallByName Num.51 Dict.501 Dict.986; - let Dict.985 : U64 = 16i64; - let Dict.975 : Int1 = CallByName Num.23 Dict.504 Dict.985; - if Dict.975 then - inc Dict.500; - let Dict.984 : U64 = 16i64; - let Dict.983 : U64 = CallByName Num.75 Dict.504 Dict.984; - let Dict.982 : U64 = CallByName Num.51 Dict.983 Dict.505; - let Dict.977 : U64 = CallByName Dict.91 Dict.500 Dict.982; - let Dict.981 : U64 = 8i64; - let Dict.980 : U64 = CallByName Num.75 Dict.504 Dict.981; - let Dict.979 : U64 = CallByName Num.51 Dict.980 Dict.505; - let Dict.978 : U64 = CallByName Dict.91 Dict.500 Dict.979; - let Dict.976 : {U64, U64, U64} = Struct {Dict.977, Dict.978, Dict.503}; - ret Dict.976; + jump Dict.927 #Derived_gen.6 #Derived_gen.7 #Derived_gen.8 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11; + +procedure Dict.91 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3): + joinpoint Dict.980 Dict.506 Dict.507 Dict.508 Dict.509: + inc 2 Dict.507; + let Dict.1000 : U64 = CallByName Dict.98 Dict.507 Dict.508; + let Dict.1001 : U64 = CallByName Dict.93; + let Dict.995 : U64 = CallByName Num.70 Dict.1000 Dict.1001; + let Dict.999 : U64 = 8i64; + let Dict.998 : U64 = CallByName Num.51 Dict.508 Dict.999; + let Dict.997 : U64 = CallByName Dict.98 Dict.507 Dict.998; + let Dict.996 : U64 = CallByName Num.70 Dict.997 Dict.506; + let Dict.510 : U64 = CallByName Dict.96 Dict.995 Dict.996; + let Dict.994 : U64 = 16i64; + let Dict.511 : U64 = CallByName Num.75 Dict.509 Dict.994; + let Dict.993 : U64 = 16i64; + let Dict.512 : U64 = CallByName Num.51 Dict.508 Dict.993; + let Dict.992 : U64 = 16i64; + let Dict.982 : Int1 = CallByName Num.23 Dict.511 Dict.992; + if Dict.982 then + inc Dict.507; + let Dict.991 : U64 = 16i64; + let Dict.990 : U64 = CallByName Num.75 Dict.511 Dict.991; + let Dict.989 : U64 = CallByName Num.51 Dict.990 Dict.512; + let Dict.984 : U64 = CallByName Dict.98 Dict.507 Dict.989; + let Dict.988 : U64 = 8i64; + let Dict.987 : U64 = CallByName Num.75 Dict.511 Dict.988; + let Dict.986 : U64 = CallByName Num.51 Dict.987 Dict.512; + let Dict.985 : U64 = CallByName Dict.98 Dict.507 Dict.986; + let Dict.983 : {U64, U64, U64} = Struct {Dict.984, Dict.985, Dict.510}; + ret Dict.983; else - jump Dict.973 Dict.503 Dict.500 Dict.505 Dict.504; + jump Dict.980 Dict.510 Dict.507 Dict.512 Dict.511; in - jump Dict.973 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46; - -procedure Dict.85 (): - let Dict.910 : U64 = 11562461410679940143i64; - ret Dict.910; - -procedure Dict.86 (): - let Dict.906 : U64 = 16646288086500911323i64; + jump Dict.980 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3; + +procedure Dict.92 (): + let Dict.917 : U64 = 11562461410679940143i64; + ret Dict.917; + +procedure Dict.93 (): + let Dict.913 : U64 = 16646288086500911323i64; + ret Dict.913; + +procedure Dict.94 (): + let Dict.1026 : U64 = 10285213230658275043i64; + ret Dict.1026; + +procedure Dict.95 (): + let Dict.1016 : U64 = 6384245875588680899i64; + ret Dict.1016; + +procedure Dict.96 (Dict.513, Dict.514): + let Dict.905 : {U64, U64} = CallByName Dict.97 Dict.513 Dict.514; + let Dict.515 : U64 = StructAtIndex 0 Dict.905; + let Dict.516 : U64 = StructAtIndex 1 Dict.905; + let Dict.904 : U64 = CallByName Num.70 Dict.515 Dict.516; + ret Dict.904; + +procedure Dict.97 (Dict.517, Dict.518): + let Dict.909 : U128 = CallByName Num.135 Dict.517; + let Dict.910 : U128 = CallByName Num.135 Dict.518; + let Dict.519 : U128 = CallByName Num.78 Dict.909 Dict.910; + let Dict.520 : U64 = CallByName Num.133 Dict.519; + let Dict.908 : U8 = 64i64; + let Dict.907 : U128 = CallByName Num.74 Dict.519 Dict.908; + let Dict.521 : U64 = CallByName Num.133 Dict.907; + let Dict.906 : {U64, U64} = Struct {Dict.520, Dict.521}; ret Dict.906; -procedure Dict.87 (): - let Dict.1019 : U64 = 10285213230658275043i64; - ret Dict.1019; - -procedure Dict.88 (): - let Dict.1009 : U64 = 6384245875588680899i64; - ret Dict.1009; - -procedure Dict.89 (Dict.506, Dict.507): - let Dict.898 : {U64, U64} = CallByName Dict.90 Dict.506 Dict.507; - let Dict.508 : U64 = StructAtIndex 0 Dict.898; - let Dict.509 : U64 = StructAtIndex 1 Dict.898; - let Dict.897 : U64 = CallByName Num.70 Dict.508 Dict.509; - ret Dict.897; - -procedure Dict.90 (Dict.510, Dict.511): - let Dict.902 : U128 = CallByName Num.135 Dict.510; - let Dict.903 : U128 = CallByName Num.135 Dict.511; - let Dict.512 : U128 = CallByName Num.78 Dict.902 Dict.903; - let Dict.513 : U64 = CallByName Num.133 Dict.512; - let Dict.901 : U8 = 64i64; - let Dict.900 : U128 = CallByName Num.74 Dict.512 Dict.901; - let Dict.514 : U64 = CallByName Num.133 Dict.900; - let Dict.899 : {U64, U64} = Struct {Dict.513, Dict.514}; - ret Dict.899; - -procedure Dict.91 (Dict.515, Dict.516): - let Dict.964 : U8 = CallByName Dict.22 Dict.515 Dict.516; - let Dict.517 : U64 = CallByName Num.133 Dict.964; - let Dict.963 : U64 = 1i64; - let Dict.962 : U64 = CallByName Num.51 Dict.516 Dict.963; - let Dict.961 : U8 = CallByName Dict.22 Dict.515 Dict.962; - let Dict.518 : U64 = CallByName Num.133 Dict.961; - let Dict.960 : U64 = 2i64; - let Dict.959 : U64 = CallByName Num.51 Dict.516 Dict.960; - let Dict.958 : U8 = CallByName Dict.22 Dict.515 Dict.959; - let Dict.519 : U64 = CallByName Num.133 Dict.958; - let Dict.957 : U64 = 3i64; - let Dict.956 : U64 = CallByName Num.51 Dict.516 Dict.957; - let Dict.955 : U8 = CallByName Dict.22 Dict.515 Dict.956; - let Dict.520 : U64 = CallByName Num.133 Dict.955; - let Dict.954 : U64 = 4i64; - let Dict.953 : U64 = CallByName Num.51 Dict.516 Dict.954; - let Dict.952 : U8 = CallByName Dict.22 Dict.515 Dict.953; - let Dict.521 : U64 = CallByName Num.133 Dict.952; - let Dict.951 : U64 = 5i64; - let Dict.950 : U64 = CallByName Num.51 Dict.516 Dict.951; - let Dict.949 : U8 = CallByName Dict.22 Dict.515 Dict.950; - let Dict.522 : U64 = CallByName Num.133 Dict.949; - let Dict.948 : U64 = 6i64; - let Dict.947 : U64 = CallByName Num.51 Dict.516 Dict.948; - let Dict.946 : U8 = CallByName Dict.22 Dict.515 Dict.947; - let Dict.523 : U64 = CallByName Num.133 Dict.946; - let Dict.945 : U64 = 7i64; - let Dict.943 : U64 = CallByName Num.51 Dict.516 Dict.945; - let Dict.942 : U8 = CallByName Dict.22 Dict.515 Dict.943; - dec Dict.515; - let Dict.524 : U64 = CallByName Num.133 Dict.942; - let Dict.941 : U8 = 8i64; - let Dict.940 : U64 = CallByName Num.72 Dict.518 Dict.941; - let Dict.525 : U64 = CallByName Num.71 Dict.517 Dict.940; - let Dict.939 : U8 = 16i64; - let Dict.936 : U64 = CallByName Num.72 Dict.519 Dict.939; - let Dict.938 : U8 = 24i64; - let Dict.937 : U64 = CallByName Num.72 Dict.520 Dict.938; - let Dict.526 : U64 = CallByName Num.71 Dict.936 Dict.937; - let Dict.935 : U8 = 32i64; - let Dict.932 : U64 = CallByName Num.72 Dict.521 Dict.935; - let Dict.934 : U8 = 40i64; - let Dict.933 : U64 = CallByName Num.72 Dict.522 Dict.934; - let Dict.527 : U64 = CallByName Num.71 Dict.932 Dict.933; - let Dict.931 : U8 = 48i64; - let Dict.928 : U64 = CallByName Num.72 Dict.523 Dict.931; - let Dict.930 : U8 = 56i64; - let Dict.929 : U64 = CallByName Num.72 Dict.524 Dict.930; - let Dict.528 : U64 = CallByName Num.71 Dict.928 Dict.929; - let Dict.926 : U64 = CallByName Num.71 Dict.525 Dict.526; - let Dict.927 : U64 = CallByName Num.71 Dict.527 Dict.528; - let Dict.925 : U64 = CallByName Num.71 Dict.926 Dict.927; - ret Dict.925; - -procedure Dict.92 (Dict.529, Dict.530): - let Dict.1076 : U8 = CallByName Dict.22 Dict.529 Dict.530; - let Dict.531 : U64 = CallByName Num.133 Dict.1076; - let Dict.1075 : U64 = 1i64; - let Dict.1074 : U64 = CallByName Num.51 Dict.530 Dict.1075; - let Dict.1073 : U8 = CallByName Dict.22 Dict.529 Dict.1074; - let Dict.532 : U64 = CallByName Num.133 Dict.1073; - let Dict.1072 : U64 = 2i64; - let Dict.1071 : U64 = CallByName Num.51 Dict.530 Dict.1072; - let Dict.1070 : U8 = CallByName Dict.22 Dict.529 Dict.1071; - let Dict.533 : U64 = CallByName Num.133 Dict.1070; - let Dict.1069 : U64 = 3i64; - let Dict.1068 : U64 = CallByName Num.51 Dict.530 Dict.1069; - let Dict.1067 : U8 = CallByName Dict.22 Dict.529 Dict.1068; - dec Dict.529; - let Dict.534 : U64 = CallByName Num.133 Dict.1067; - let Dict.1066 : U8 = 8i64; - let Dict.1065 : U64 = CallByName Num.72 Dict.532 Dict.1066; - let Dict.535 : U64 = CallByName Num.71 Dict.531 Dict.1065; - let Dict.1064 : U8 = 16i64; - let Dict.1061 : U64 = CallByName Num.72 Dict.533 Dict.1064; - let Dict.1063 : U8 = 24i64; - let Dict.1062 : U64 = CallByName Num.72 Dict.534 Dict.1063; - let Dict.536 : U64 = CallByName Num.71 Dict.1061 Dict.1062; - let Dict.1060 : U64 = CallByName Num.71 Dict.535 Dict.536; - ret Dict.1060; - -procedure Dict.93 (Dict.537, Dict.538, Dict.539): - let Dict.1054 : U8 = CallByName Dict.22 Dict.537 Dict.538; - let Dict.540 : U64 = CallByName Num.133 Dict.1054; - let Dict.1053 : U8 = 1i64; - let Dict.1052 : U64 = CallByName Num.74 Dict.539 Dict.1053; - let Dict.1051 : U64 = CallByName Num.51 Dict.1052 Dict.538; - let Dict.1050 : U8 = CallByName Dict.22 Dict.537 Dict.1051; - let Dict.541 : U64 = CallByName Num.133 Dict.1050; - let Dict.1049 : U64 = 1i64; - let Dict.1048 : U64 = CallByName Num.75 Dict.539 Dict.1049; - let Dict.1047 : U64 = CallByName Num.51 Dict.1048 Dict.538; - let Dict.1046 : U8 = CallByName Dict.22 Dict.537 Dict.1047; - dec Dict.537; - let Dict.542 : U64 = CallByName Num.133 Dict.1046; - let Dict.1045 : U8 = 16i64; - let Dict.1042 : U64 = CallByName Num.72 Dict.540 Dict.1045; - let Dict.1044 : U8 = 8i64; - let Dict.1043 : U64 = CallByName Num.72 Dict.541 Dict.1044; - let Dict.543 : U64 = CallByName Num.71 Dict.1042 Dict.1043; - let Dict.1041 : U64 = CallByName Num.71 Dict.543 Dict.542; - ret Dict.1041; - -procedure Hash.19 (Hash.38, Hash.39): - let Hash.71 : List U8 = CallByName Str.12 Hash.39; - let Hash.70 : {U64, U64} = CallByName Dict.82 Hash.38 Hash.71; - ret Hash.70; - -procedure Inspect.183 (Inspect.184, #Attr.12): - let Inspect.182 : {} = StructAtIndex 3 #Attr.12; - let Inspect.181 : {} = StructAtIndex 2 #Attr.12; - let Inspect.180 : {} = StructAtIndex 1 #Attr.12; - let Inspect.179 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = StructAtIndex 0 #Attr.12; - let Inspect.351 : Str = "{"; - let Inspect.324 : Str = CallByName Inspect.59 Inspect.184 Inspect.351; - let Inspect.325 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = Struct {Inspect.179, Inspect.180, Inspect.181, Inspect.182}; - let Inspect.320 : {Str, Int1} = CallByName Inspect.185 Inspect.324 Inspect.325; - let Inspect.321 : {} = Struct {}; - let Inspect.316 : Str = CallByName Inspect.197 Inspect.320; - let Inspect.317 : Str = "}"; - let Inspect.315 : Str = CallByName Inspect.59 Inspect.316 Inspect.317; - ret Inspect.315; - -procedure Inspect.185 (Inspect.186, #Attr.12): - let Inspect.182 : {} = StructAtIndex 3 #Attr.12; - let Inspect.181 : {} = StructAtIndex 2 #Attr.12; - let Inspect.180 : {} = StructAtIndex 1 #Attr.12; - let Inspect.179 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = StructAtIndex 0 #Attr.12; - let Inspect.350 : Int1 = CallByName Bool.1; - let Inspect.328 : {Str, Int1} = Struct {Inspect.186, Inspect.350}; - let Inspect.329 : {{}, {}} = Struct {Inspect.181, Inspect.182}; - let Inspect.327 : {Str, Int1} = CallByName Dict.10 Inspect.179 Inspect.328 Inspect.329; - ret Inspect.327; +procedure Dict.98 (Dict.522, Dict.523): + let Dict.971 : U8 = CallByName Dict.22 Dict.522 Dict.523; + let Dict.524 : U64 = CallByName Num.133 Dict.971; + let Dict.970 : U64 = 1i64; + let Dict.969 : U64 = CallByName Num.51 Dict.523 Dict.970; + let Dict.968 : U8 = CallByName Dict.22 Dict.522 Dict.969; + let Dict.525 : U64 = CallByName Num.133 Dict.968; + let Dict.967 : U64 = 2i64; + let Dict.966 : U64 = CallByName Num.51 Dict.523 Dict.967; + let Dict.965 : U8 = CallByName Dict.22 Dict.522 Dict.966; + let Dict.526 : U64 = CallByName Num.133 Dict.965; + let Dict.964 : U64 = 3i64; + let Dict.963 : U64 = CallByName Num.51 Dict.523 Dict.964; + let Dict.962 : U8 = CallByName Dict.22 Dict.522 Dict.963; + let Dict.527 : U64 = CallByName Num.133 Dict.962; + let Dict.961 : U64 = 4i64; + let Dict.960 : U64 = CallByName Num.51 Dict.523 Dict.961; + let Dict.959 : U8 = CallByName Dict.22 Dict.522 Dict.960; + let Dict.528 : U64 = CallByName Num.133 Dict.959; + let Dict.958 : U64 = 5i64; + let Dict.957 : U64 = CallByName Num.51 Dict.523 Dict.958; + let Dict.956 : U8 = CallByName Dict.22 Dict.522 Dict.957; + let Dict.529 : U64 = CallByName Num.133 Dict.956; + let Dict.955 : U64 = 6i64; + let Dict.954 : U64 = CallByName Num.51 Dict.523 Dict.955; + let Dict.953 : U8 = CallByName Dict.22 Dict.522 Dict.954; + let Dict.530 : U64 = CallByName Num.133 Dict.953; + let Dict.952 : U64 = 7i64; + let Dict.950 : U64 = CallByName Num.51 Dict.523 Dict.952; + let Dict.949 : U8 = CallByName Dict.22 Dict.522 Dict.950; + dec Dict.522; + let Dict.531 : U64 = CallByName Num.133 Dict.949; + let Dict.948 : U8 = 8i64; + let Dict.947 : U64 = CallByName Num.72 Dict.525 Dict.948; + let Dict.532 : U64 = CallByName Num.71 Dict.524 Dict.947; + let Dict.946 : U8 = 16i64; + let Dict.943 : U64 = CallByName Num.72 Dict.526 Dict.946; + let Dict.945 : U8 = 24i64; + let Dict.944 : U64 = CallByName Num.72 Dict.527 Dict.945; + let Dict.533 : U64 = CallByName Num.71 Dict.943 Dict.944; + let Dict.942 : U8 = 32i64; + let Dict.939 : U64 = CallByName Num.72 Dict.528 Dict.942; + let Dict.941 : U8 = 40i64; + let Dict.940 : U64 = CallByName Num.72 Dict.529 Dict.941; + let Dict.534 : U64 = CallByName Num.71 Dict.939 Dict.940; + let Dict.938 : U8 = 48i64; + let Dict.935 : U64 = CallByName Num.72 Dict.530 Dict.938; + let Dict.937 : U8 = 56i64; + let Dict.936 : U64 = CallByName Num.72 Dict.531 Dict.937; + let Dict.535 : U64 = CallByName Num.71 Dict.935 Dict.936; + let Dict.933 : U64 = CallByName Num.71 Dict.532 Dict.533; + let Dict.934 : U64 = CallByName Num.71 Dict.534 Dict.535; + let Dict.932 : U64 = CallByName Num.71 Dict.933 Dict.934; + ret Dict.932; + +procedure Dict.99 (Dict.536, Dict.537): + let Dict.1083 : U8 = CallByName Dict.22 Dict.536 Dict.537; + let Dict.538 : U64 = CallByName Num.133 Dict.1083; + let Dict.1082 : U64 = 1i64; + let Dict.1081 : U64 = CallByName Num.51 Dict.537 Dict.1082; + let Dict.1080 : U8 = CallByName Dict.22 Dict.536 Dict.1081; + let Dict.539 : U64 = CallByName Num.133 Dict.1080; + let Dict.1079 : U64 = 2i64; + let Dict.1078 : U64 = CallByName Num.51 Dict.537 Dict.1079; + let Dict.1077 : U8 = CallByName Dict.22 Dict.536 Dict.1078; + let Dict.540 : U64 = CallByName Num.133 Dict.1077; + let Dict.1076 : U64 = 3i64; + let Dict.1075 : U64 = CallByName Num.51 Dict.537 Dict.1076; + let Dict.1074 : U8 = CallByName Dict.22 Dict.536 Dict.1075; + dec Dict.536; + let Dict.541 : U64 = CallByName Num.133 Dict.1074; + let Dict.1073 : U8 = 8i64; + let Dict.1072 : U64 = CallByName Num.72 Dict.539 Dict.1073; + let Dict.542 : U64 = CallByName Num.71 Dict.538 Dict.1072; + let Dict.1071 : U8 = 16i64; + let Dict.1068 : U64 = CallByName Num.72 Dict.540 Dict.1071; + let Dict.1070 : U8 = 24i64; + let Dict.1069 : U64 = CallByName Num.72 Dict.541 Dict.1070; + let Dict.543 : U64 = CallByName Num.71 Dict.1068 Dict.1069; + let Dict.1067 : U64 = CallByName Num.71 Dict.542 Dict.543; + ret Dict.1067; + +procedure Hash.19 (Hash.42, Hash.43): + let Hash.75 : List U8 = CallByName Str.12 Hash.43; + let Hash.74 : {U64, U64} = CallByName Dict.89 Hash.42 Hash.75; + ret Hash.74; + +procedure Inspect.187 (Inspect.188, #Attr.12): + let Inspect.186 : {} = StructAtIndex 3 #Attr.12; + let Inspect.185 : {} = StructAtIndex 2 #Attr.12; + let Inspect.184 : {} = StructAtIndex 1 #Attr.12; + let Inspect.183 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = StructAtIndex 0 #Attr.12; + let Inspect.355 : Str = "{"; + let Inspect.328 : Str = CallByName Inspect.63 Inspect.188 Inspect.355; + let Inspect.329 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = Struct {Inspect.183, Inspect.184, Inspect.185, Inspect.186}; + let Inspect.324 : {Str, Int1} = CallByName Inspect.189 Inspect.328 Inspect.329; + let Inspect.325 : {} = Struct {}; + let Inspect.320 : Str = CallByName Inspect.201 Inspect.324; + let Inspect.321 : Str = "}"; + let Inspect.319 : Str = CallByName Inspect.63 Inspect.320 Inspect.321; + ret Inspect.319; -procedure Inspect.187 (Inspect.330, Inspect.190, Inspect.191, #Attr.12): - let Inspect.182 : {} = StructAtIndex 1 #Attr.12; - let Inspect.181 : {} = StructAtIndex 0 #Attr.12; - let Inspect.188 : Str = StructAtIndex 0 Inspect.330; - let Inspect.189 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.348 Inspect.192: - let Inspect.345 : Str = CallByName Inspect.43 Inspect.190; - let Inspect.343 : Str = CallByName Inspect.31 Inspect.345 Inspect.192; - let Inspect.344 : Str = ": "; - let Inspect.337 : Str = CallByName Inspect.59 Inspect.343 Inspect.344; - let Inspect.338 : {I64, {}} = Struct {Inspect.191, Inspect.182}; - let Inspect.333 : Str = CallByName Inspect.193 Inspect.337 Inspect.338; - let Inspect.334 : {} = Struct {}; - let Inspect.332 : {Str, Int1} = CallByName Inspect.195 Inspect.333; - ret Inspect.332; +procedure Inspect.189 (Inspect.190, #Attr.12): + let Inspect.186 : {} = StructAtIndex 3 #Attr.12; + let Inspect.185 : {} = StructAtIndex 2 #Attr.12; + let Inspect.184 : {} = StructAtIndex 1 #Attr.12; + let Inspect.183 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = StructAtIndex 0 #Attr.12; + let Inspect.354 : Int1 = CallByName Bool.1; + let Inspect.332 : {Str, Int1} = Struct {Inspect.190, Inspect.354}; + let Inspect.333 : {{}, {}} = Struct {Inspect.185, Inspect.186}; + let Inspect.331 : {Str, Int1} = CallByName Dict.10 Inspect.183 Inspect.332 Inspect.333; + ret Inspect.331; + +procedure Inspect.191 (Inspect.334, Inspect.194, Inspect.195, #Attr.12): + let Inspect.186 : {} = StructAtIndex 1 #Attr.12; + let Inspect.185 : {} = StructAtIndex 0 #Attr.12; + let Inspect.192 : Str = StructAtIndex 0 Inspect.334; + let Inspect.193 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.352 Inspect.196: + let Inspect.349 : Str = CallByName Inspect.47 Inspect.194; + let Inspect.347 : Str = CallByName Inspect.31 Inspect.349 Inspect.196; + let Inspect.348 : Str = ": "; + let Inspect.341 : Str = CallByName Inspect.63 Inspect.347 Inspect.348; + let Inspect.342 : {I64, {}} = Struct {Inspect.195, Inspect.186}; + let Inspect.337 : Str = CallByName Inspect.197 Inspect.341 Inspect.342; + let Inspect.338 : {} = Struct {}; + let Inspect.336 : {Str, Int1} = CallByName Inspect.199 Inspect.337; + ret Inspect.336; in - if Inspect.189 then - let Inspect.349 : Str = ", "; - let Inspect.347 : Str = CallByName Inspect.59 Inspect.188 Inspect.349; - jump Inspect.348 Inspect.347; + if Inspect.193 then + let Inspect.353 : Str = ", "; + let Inspect.351 : Str = CallByName Inspect.63 Inspect.192 Inspect.353; + jump Inspect.352 Inspect.351; else - jump Inspect.348 Inspect.188; - -procedure Inspect.193 (Inspect.194, #Attr.12): - let Inspect.182 : {} = StructAtIndex 1 #Attr.12; - let Inspect.191 : I64 = StructAtIndex 0 #Attr.12; - let Inspect.341 : I64 = CallByName Inspect.53 Inspect.191; - let Inspect.340 : Str = CallByName Inspect.31 Inspect.341 Inspect.194; - ret Inspect.340; - -procedure Inspect.195 (Inspect.196): - let Inspect.336 : Int1 = CallByName Bool.2; - let Inspect.335 : {Str, Int1} = Struct {Inspect.196, Inspect.336}; - ret Inspect.335; - -procedure Inspect.197 (Inspect.322): - let Inspect.323 : Str = StructAtIndex 0 Inspect.322; - ret Inspect.323; - -procedure Inspect.246 (Inspect.247, Inspect.245): - let Inspect.366 : Str = "\""; - let Inspect.365 : Str = CallByName Inspect.59 Inspect.247 Inspect.366; - let Inspect.363 : Str = CallByName Inspect.59 Inspect.365 Inspect.245; - let Inspect.364 : Str = "\""; - let Inspect.362 : Str = CallByName Inspect.59 Inspect.363 Inspect.364; - ret Inspect.362; + jump Inspect.352 Inspect.192; + +procedure Inspect.197 (Inspect.198, #Attr.12): + let Inspect.186 : {} = StructAtIndex 1 #Attr.12; + let Inspect.195 : I64 = StructAtIndex 0 #Attr.12; + let Inspect.345 : I64 = CallByName Inspect.57 Inspect.195; + let Inspect.344 : Str = CallByName Inspect.31 Inspect.345 Inspect.198; + ret Inspect.344; + +procedure Inspect.199 (Inspect.200): + let Inspect.340 : Int1 = CallByName Bool.2; + let Inspect.339 : {Str, Int1} = Struct {Inspect.200, Inspect.340}; + ret Inspect.339; + +procedure Inspect.201 (Inspect.326): + let Inspect.327 : Str = StructAtIndex 0 Inspect.326; + ret Inspect.327; -procedure Inspect.274 (Inspect.275, Inspect.273): - let Inspect.357 : Str = CallByName Num.96 Inspect.273; - let Inspect.356 : Str = CallByName Inspect.59 Inspect.275 Inspect.357; - ret Inspect.356; +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.370 : Str = "\""; + let Inspect.369 : Str = CallByName Inspect.63 Inspect.251 Inspect.370; + let Inspect.367 : Str = CallByName Inspect.63 Inspect.369 Inspect.249; + let Inspect.368 : Str = "\""; + let Inspect.366 : Str = CallByName Inspect.63 Inspect.367 Inspect.368; + ret Inspect.366; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.278 (Inspect.279, Inspect.277): + let Inspect.361 : Str = CallByName Num.96 Inspect.277; + let Inspect.360 : Str = CallByName Inspect.63 Inspect.279 Inspect.361; + ret Inspect.360; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.310 : Str = CallByName Inspect.183 Inspect.145 Inspect.299; - ret Inspect.310; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.342 : Str = CallByName Inspect.274 Inspect.145 Inspect.299; - ret Inspect.342; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.314 : Str = CallByName Inspect.187 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.346 : Str = CallByName Inspect.246 Inspect.145 Inspect.299; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.346 : Str = CallByName Inspect.278 Inspect.149 Inspect.303; ret Inspect.346; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.350 : Str = CallByName Inspect.250 Inspect.149 Inspect.303; + ret Inspect.350; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.38 (Inspect.179, Inspect.180, Inspect.181, Inspect.182): - let Inspect.312 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = Struct {Inspect.179, Inspect.180, Inspect.181, Inspect.182}; - let Inspect.311 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = CallByName Inspect.30 Inspect.312; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; ret Inspect.311; -procedure Inspect.43 (Inspect.245): - let Inspect.358 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.358; - -procedure Inspect.5 (Inspect.146): - let Inspect.308 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.36 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName Dict.120 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.42 (Inspect.183, Inspect.184, Inspect.185, Inspect.186): + let Inspect.316 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = Struct {Inspect.183, Inspect.184, Inspect.185, Inspect.186}; + let Inspect.315 : {{List {U32, U32}, List {Str, I64}, U64, Float32, U8}, {}, {}, {}} = CallByName Inspect.30 Inspect.316; + ret Inspect.315; -procedure Inspect.53 (Inspect.273): - let Inspect.352 : I64 = CallByName Inspect.30 Inspect.273; - ret Inspect.352; +procedure Inspect.47 (Inspect.249): + let Inspect.362 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.362; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.319 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.319; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.43 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName Dict.127 Inspect.308 Inspect.312; + ret Inspect.307; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.57 (Inspect.277): + let Inspect.356 : I64 = CallByName Inspect.30 Inspect.277; + ret Inspect.356; -procedure List.11 (List.138, List.139): - let List.636 : List {U32, U32} = CallByName List.68 List.139; - let List.635 : List {U32, U32} = CallByName List.90 List.138 List.139 List.636; - ret List.635; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.323 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.323; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.18 (List.160, List.161, List.162): - let List.639 : U64 = 0i64; - let List.640 : U64 = CallByName List.6 List.160; - let List.638 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.639 List.640; +procedure List.11 (List.141, List.142): + let List.639 : List {U32, U32} = CallByName List.68 List.142; + let List.638 : List {U32, U32} = CallByName List.93 List.141 List.142 List.639; ret List.638; -procedure List.3 (List.116, List.117, List.118): - let List.600 : {List {U32, U32}, {U32, U32}} = CallByName List.64 List.116 List.117 List.118; - let List.599 : List {U32, U32} = StructAtIndex 0 List.600; - ret List.599; - -procedure List.3 (List.116, List.117, List.118): - let List.602 : {List {Str, I64}, {Str, I64}} = CallByName List.64 List.116 List.117 List.118; - let List.601 : List {Str, I64} = StructAtIndex 0 List.602; - let #Derived_gen.71 : {Str, I64} = StructAtIndex 1 List.602; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; + +procedure List.18 (List.163, List.164, List.165): + let List.642 : U64 = 0i64; + let List.643 : U64 = CallByName List.6 List.163; + let List.641 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.642 List.643; + ret List.641; + +procedure List.3 (List.119, List.120, List.121): + let List.603 : {List {U32, U32}, {U32, U32}} = CallByName List.64 List.119 List.120 List.121; + let List.602 : List {U32, U32} = StructAtIndex 0 List.603; + ret List.602; + +procedure List.3 (List.119, List.120, List.121): + let List.605 : {List {Str, I64}, {Str, I64}} = CallByName List.64 List.119 List.120 List.121; + let List.604 : List {Str, I64} = StructAtIndex 0 List.605; + let #Derived_gen.71 : {Str, I64} = StructAtIndex 1 List.605; dec #Derived_gen.71; - ret List.601; + ret List.604; -procedure List.4 (List.124, List.125): - let List.611 : U64 = 1i64; - let List.609 : List {Str, I64} = CallByName List.70 List.124 List.611; - let List.608 : List {Str, I64} = CallByName List.71 List.609 List.125; - ret List.608; +procedure List.4 (List.127, List.128): + let List.614 : U64 = 1i64; + let List.612 : List {Str, I64} = CallByName List.70 List.127 List.614; + let List.611 : List {Str, I64} = CallByName List.71 List.612 List.128; + ret List.611; procedure List.6 (#Attr.2): - let List.590 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.590; + let List.593 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.593; procedure List.6 (#Attr.2): - let List.637 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.637; + let List.640 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.640; procedure List.6 (#Attr.2): - let List.649 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.649; - -procedure List.64 (List.113, List.114, List.115): - let List.598 : U64 = CallByName List.6 List.113; - let List.595 : Int1 = CallByName Num.22 List.114 List.598; - if List.595 then - let List.596 : {List {U32, U32}, {U32, U32}} = CallByName List.67 List.113 List.114 List.115; - ret List.596; + let List.652 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.652; + +procedure List.64 (List.116, List.117, List.118): + let List.601 : U64 = CallByName List.6 List.116; + let List.598 : Int1 = CallByName Num.22 List.117 List.601; + if List.598 then + let List.599 : {List {U32, U32}, {U32, U32}} = CallByName List.67 List.116 List.117 List.118; + ret List.599; else - let List.594 : {List {U32, U32}, {U32, U32}} = Struct {List.113, List.115}; - ret List.594; - -procedure List.64 (List.113, List.114, List.115): - let List.607 : U64 = CallByName List.6 List.113; - let List.604 : Int1 = CallByName Num.22 List.114 List.607; - if List.604 then - let List.605 : {List {Str, I64}, {Str, I64}} = CallByName List.67 List.113 List.114 List.115; - ret List.605; + let List.597 : {List {U32, U32}, {U32, U32}} = Struct {List.116, List.118}; + ret List.597; + +procedure List.64 (List.116, List.117, List.118): + let List.610 : U64 = CallByName List.6 List.116; + let List.607 : Int1 = CallByName Num.22 List.117 List.610; + if List.607 then + let List.608 : {List {Str, I64}, {Str, I64}} = CallByName List.67 List.116 List.117 List.118; + ret List.608; else - let List.603 : {List {Str, I64}, {Str, I64}} = Struct {List.113, List.115}; - ret List.603; + let List.606 : {List {Str, I64}, {Str, I64}} = Struct {List.116, List.118}; + ret List.606; procedure List.66 (#Attr.2, #Attr.3): - let List.648 : {Str, I64} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.648; + let List.651 : {Str, I64} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.651; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.597 : {List {U32, U32}, {U32, U32}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.597; + let List.600 : {List {U32, U32}, {U32, U32}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.600; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.606 : {List {Str, I64}, {Str, I64}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.606; + let List.609 : {List {Str, I64}, {Str, I64}} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.609; procedure List.68 (#Attr.2): - let List.634 : List {U32, U32} = lowlevel ListWithCapacity #Attr.2; - ret List.634; + let List.637 : List {U32, U32} = lowlevel ListWithCapacity #Attr.2; + ret List.637; procedure List.70 (#Attr.2, #Attr.3): - let List.612 : List {Str, I64} = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.612; + let List.615 : List {Str, I64} = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.615; procedure List.71 (#Attr.2, #Attr.3): - let List.610 : List {Str, I64} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.610; + let List.613 : List {Str, I64} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.613; procedure List.71 (#Attr.2, #Attr.3): - let List.631 : List {U32, U32} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.631; - -procedure List.83 (List.169, List.170, List.171): - let List.614 : U64 = 0i64; - let List.615 : U64 = CallByName List.6 List.169; - let List.613 : List {U32, U32} = CallByName List.93 List.169 List.170 List.171 List.614 List.615; - ret List.613; + let List.634 : List {U32, U32} = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.634; -procedure List.90 (#Derived_gen.33, #Derived_gen.34, #Derived_gen.35): - joinpoint List.625 List.140 List.141 List.142: - let List.633 : U64 = 0i64; - let List.627 : Int1 = CallByName Num.24 List.141 List.633; - if List.627 then - let List.632 : U64 = 1i64; - let List.629 : U64 = CallByName Num.75 List.141 List.632; - let List.630 : List {U32, U32} = CallByName List.71 List.142 List.140; - jump List.625 List.140 List.629 List.630; +procedure List.83 (List.172, List.173, List.174): + let List.617 : U64 = 0i64; + let List.618 : U64 = CallByName List.6 List.172; + let List.616 : List {U32, U32} = CallByName List.96 List.172 List.173 List.174 List.617 List.618; + ret List.616; + +procedure List.93 (#Derived_gen.63, #Derived_gen.64, #Derived_gen.65): + joinpoint List.628 List.143 List.144 List.145: + let List.636 : U64 = 0i64; + let List.630 : Int1 = CallByName Num.24 List.144 List.636; + if List.630 then + let List.635 : U64 = 1i64; + let List.632 : U64 = CallByName Num.75 List.144 List.635; + let List.633 : List {U32, U32} = CallByName List.71 List.145 List.143; + jump List.628 List.143 List.632 List.633; else - ret List.142; + ret List.145; in - jump List.625 #Derived_gen.33 #Derived_gen.34 #Derived_gen.35; - -procedure List.92 (#Derived_gen.50, #Derived_gen.51, #Derived_gen.52, #Derived_gen.53, #Derived_gen.54): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, I64} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.152 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + jump List.628 #Derived_gen.63 #Derived_gen.64 #Derived_gen.65; + +procedure List.95 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): + joinpoint List.644 List.166 List.167 List.168 List.169 List.170: + let List.646 : Int1 = CallByName Num.22 List.169 List.170; + if List.646 then + let List.650 : {Str, I64} = CallByName List.66 List.166 List.169; + inc List.650; + let List.171 : {Str, Int1} = CallByName Dict.188 List.167 List.650 List.168; + let List.649 : U64 = 1i64; + let List.648 : U64 = CallByName Num.51 List.169 List.649; + jump List.644 List.166 List.171 List.168 List.648 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.50 #Derived_gen.51 #Derived_gen.52 #Derived_gen.53 #Derived_gen.54; - -procedure List.92 (#Derived_gen.59, #Derived_gen.60, #Derived_gen.61, #Derived_gen.62, #Derived_gen.63): - joinpoint List.641 List.163 List.164 List.165 List.166 List.167: - let List.643 : Int1 = CallByName Num.22 List.166 List.167; - if List.643 then - let List.647 : {Str, I64} = CallByName List.66 List.163 List.166; - inc List.647; - let List.168 : {Str, Int1} = CallByName Dict.181 List.164 List.647 List.165; - let List.646 : U64 = 1i64; - let List.645 : U64 = CallByName Num.51 List.166 List.646; - jump List.641 List.163 List.168 List.165 List.645 List.167; + jump List.644 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; + +procedure List.95 (#Derived_gen.37, #Derived_gen.38, #Derived_gen.39, #Derived_gen.40, #Derived_gen.41): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, I64} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : {List {U32, U32}, List {Str, I64}, U64, Float32, U8} = CallByName Dict.159 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.641 #Derived_gen.59 #Derived_gen.60 #Derived_gen.61 #Derived_gen.62 #Derived_gen.63; - -procedure List.93 (#Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26, #Derived_gen.27): - joinpoint List.616 List.172 List.173 List.174 List.175 List.176: - let List.618 : Int1 = CallByName Num.22 List.175 List.176; - if List.618 then - let List.622 : {Str, I64} = CallByName List.66 List.172 List.175; - inc List.622; - let List.177 : List {U32, U32} = CallByName Dict.398 List.173 List.622 List.175 List.174; - let List.621 : U64 = 1i64; - let List.620 : U64 = CallByName Num.51 List.175 List.621; - jump List.616 List.172 List.177 List.174 List.620 List.176; + jump List.580 #Derived_gen.37 #Derived_gen.38 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41; + +procedure List.96 (#Derived_gen.56, #Derived_gen.57, #Derived_gen.58, #Derived_gen.59, #Derived_gen.60): + joinpoint List.619 List.175 List.176 List.177 List.178 List.179: + let List.621 : Int1 = CallByName Num.22 List.178 List.179; + if List.621 then + let List.625 : {Str, I64} = CallByName List.66 List.175 List.178; + inc List.625; + let List.180 : List {U32, U32} = CallByName Dict.405 List.176 List.625 List.178 List.177; + let List.624 : U64 = 1i64; + let List.623 : U64 = CallByName Num.51 List.178 List.624; + jump List.619 List.175 List.180 List.177 List.623 List.179; else - dec List.172; - ret List.173; + dec List.175; + ret List.176; in - jump List.616 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26 #Derived_gen.27; + jump List.619 #Derived_gen.56 #Derived_gen.57 #Derived_gen.58 #Derived_gen.59 #Derived_gen.60; procedure Num.131 (#Attr.2): - let Num.287 : U32 = lowlevel NumIntCast #Attr.2; - ret Num.287; + let Num.289 : U32 = lowlevel NumIntCast #Attr.2; + ret Num.289; procedure Num.133 (#Attr.2): - let Num.295 : U64 = lowlevel NumIntCast #Attr.2; - ret Num.295; + let Num.297 : U64 = lowlevel NumIntCast #Attr.2; + ret Num.297; procedure Num.133 (#Attr.2): - let Num.344 : U64 = lowlevel NumIntCast #Attr.2; - ret Num.344; + let Num.346 : U64 = lowlevel NumIntCast #Attr.2; + ret Num.346; procedure Num.133 (#Attr.2): - let Num.359 : U64 = lowlevel NumIntCast #Attr.2; - ret Num.359; + let Num.361 : U64 = lowlevel NumIntCast #Attr.2; + ret Num.361; procedure Num.135 (#Attr.2): - let Num.365 : U128 = lowlevel NumIntCast #Attr.2; - ret Num.365; + let Num.367 : U128 = lowlevel NumIntCast #Attr.2; + ret Num.367; procedure Num.139 (#Attr.2): - let Num.313 : Float32 = lowlevel NumToFloatCast #Attr.2; - ret Num.313; + let Num.315 : Float32 = lowlevel NumToFloatCast #Attr.2; + ret Num.315; -procedure Num.148 (Num.223, Num.224): - let Num.315 : Int1 = CallByName Num.22 Num.223 Num.224; - if Num.315 then - ret Num.223; +procedure Num.148 (Num.225, Num.226): + let Num.317 : Int1 = CallByName Num.22 Num.225 Num.226; + if Num.317 then + ret Num.225; else - ret Num.224; + ret Num.226; procedure Num.21 (#Attr.2, #Attr.3): - let Num.312 : Float32 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.312; + let Num.314 : Float32 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.314; procedure Num.22 (#Attr.2, #Attr.3): - let Num.309 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.309; + let Num.311 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.311; procedure Num.22 (#Attr.2, #Attr.3): - let Num.438 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.438; + let Num.440 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.440; procedure Num.23 (#Attr.2, #Attr.3): - let Num.431 : Int1 = lowlevel NumLte #Attr.2 #Attr.3; - ret Num.431; + let Num.433 : Int1 = lowlevel NumLte #Attr.2 #Attr.3; + ret Num.433; procedure Num.24 (#Attr.2, #Attr.3): - let Num.293 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; - ret Num.293; + let Num.295 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.295; procedure Num.24 (#Attr.2, #Attr.3): - let Num.434 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; - ret Num.434; + let Num.436 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.436; procedure Num.25 (#Attr.2, #Attr.3): - let Num.435 : Int1 = lowlevel NumGte #Attr.2 #Attr.3; - ret Num.435; + let Num.437 : Int1 = lowlevel NumGte #Attr.2 #Attr.3; + ret Num.437; procedure Num.50 (#Attr.2): - let Num.311 : U64 = lowlevel NumFloor #Attr.2; - ret Num.311; + let Num.313 : U64 = lowlevel NumFloor #Attr.2; + ret Num.313; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U32 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U32 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Num.51 (#Attr.2, #Attr.3): - let Num.437 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.437; + let Num.439 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.439; procedure Num.69 (#Attr.2, #Attr.3): - let Num.301 : U32 = lowlevel NumBitwiseAnd #Attr.2 #Attr.3; - ret Num.301; + let Num.303 : U32 = lowlevel NumBitwiseAnd #Attr.2 #Attr.3; + ret Num.303; procedure Num.70 (#Attr.2, #Attr.3): - let Num.342 : U64 = lowlevel NumBitwiseXor #Attr.2 #Attr.3; - ret Num.342; + let Num.344 : U64 = lowlevel NumBitwiseXor #Attr.2 #Attr.3; + ret Num.344; procedure Num.71 (#Attr.2, #Attr.3): - let Num.300 : U32 = lowlevel NumBitwiseOr #Attr.2 #Attr.3; - ret Num.300; + let Num.302 : U32 = lowlevel NumBitwiseOr #Attr.2 #Attr.3; + ret Num.302; procedure Num.71 (#Attr.2, #Attr.3): - let Num.379 : U64 = lowlevel NumBitwiseOr #Attr.2 #Attr.3; - ret Num.379; + let Num.381 : U64 = lowlevel NumBitwiseOr #Attr.2 #Attr.3; + ret Num.381; procedure Num.72 (#Attr.2, #Attr.3): - let Num.282 : U32 = lowlevel NumShiftLeftBy #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : U32 = lowlevel NumShiftLeftBy #Attr.2 #Attr.3; + ret Num.284; procedure Num.72 (#Attr.2, #Attr.3): - let Num.394 : U64 = lowlevel NumShiftLeftBy #Attr.2 #Attr.3; - ret Num.394; + let Num.396 : U64 = lowlevel NumShiftLeftBy #Attr.2 #Attr.3; + ret Num.396; procedure Num.74 (#Attr.2, #Attr.3): - let Num.360 : U128 = lowlevel NumShiftRightZfBy #Attr.2 #Attr.3; - ret Num.360; + let Num.362 : U128 = lowlevel NumShiftRightZfBy #Attr.2 #Attr.3; + ret Num.362; procedure Num.74 (#Attr.2, #Attr.3): - let Num.362 : U64 = lowlevel NumShiftRightZfBy #Attr.2 #Attr.3; - ret Num.362; + let Num.364 : U64 = lowlevel NumShiftRightZfBy #Attr.2 #Attr.3; + ret Num.364; procedure Num.75 (#Attr.2, #Attr.3): - let Num.289 : U32 = lowlevel NumSubWrap #Attr.2 #Attr.3; - ret Num.289; + let Num.291 : U32 = lowlevel NumSubWrap #Attr.2 #Attr.3; + ret Num.291; procedure Num.75 (#Attr.2, #Attr.3): - let Num.292 : U8 = lowlevel NumSubWrap #Attr.2 #Attr.3; - ret Num.292; + let Num.294 : U8 = lowlevel NumSubWrap #Attr.2 #Attr.3; + ret Num.294; procedure Num.75 (#Attr.2, #Attr.3): - let Num.428 : U64 = lowlevel NumSubWrap #Attr.2 #Attr.3; - ret Num.428; + let Num.430 : U64 = lowlevel NumSubWrap #Attr.2 #Attr.3; + ret Num.430; procedure Num.78 (#Attr.2, #Attr.3): - let Num.363 : U128 = lowlevel NumMulWrap #Attr.2 #Attr.3; - ret Num.363; + let Num.365 : U128 = lowlevel NumMulWrap #Attr.2 #Attr.3; + ret Num.365; procedure Num.96 (#Attr.2): - let Num.308 : Str = lowlevel NumToStr #Attr.2; - ret Num.308; + let Num.310 : Str = lowlevel NumToStr #Attr.2; + ret Num.310; procedure Num.96 (#Attr.2): - let Num.436 : Str = lowlevel NumToStr #Attr.2; - ret Num.436; + let Num.438 : Str = lowlevel NumToStr #Attr.2; + ret Num.438; procedure Str.12 (#Attr.2): - let Str.234 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.234; + let Str.238 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.238; procedure Str.3 (#Attr.2, #Attr.3): - let Str.235 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.235; + let Str.239 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.239; procedure Test.0 (): let Test.8 : Str = "a"; diff --git a/crates/compiler/test_mono/generated/inspect_derived_list.txt b/crates/compiler/test_mono/generated/inspect_derived_list.txt index 0eb008dab26..561697df10e 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_list.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_list.txt @@ -3,13 +3,13 @@ procedure #Derived.0 (#Derived.1): ret #Derived_gen.0; procedure #Derived.3 (#Derived.2): - let #Derived_gen.7 : I64 = CallByName Inspect.53 #Derived.2; + let #Derived_gen.7 : I64 = CallByName Inspect.57 #Derived.2; ret #Derived_gen.7; procedure #Derived.4 (#Derived.5, #Derived.1): let #Derived_gen.5 : {} = Struct {}; let #Derived_gen.6 : {} = Struct {}; - let #Derived_gen.4 : {List I64, {}, {}} = CallByName Inspect.36 #Derived.1 #Derived_gen.5 #Derived_gen.6; + let #Derived_gen.4 : {List I64, {}, {}} = CallByName Inspect.40 #Derived.1 #Derived_gen.5 #Derived_gen.6; let #Derived_gen.3 : Str = CallByName Inspect.31 #Derived_gen.4 #Derived.5; ret #Derived_gen.3; @@ -21,154 +21,154 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Inspect.152 (Inspect.153, #Attr.12): - let Inspect.151 : {} = StructAtIndex 2 #Attr.12; - let Inspect.150 : {} = StructAtIndex 1 #Attr.12; - let Inspect.149 : List I64 = StructAtIndex 0 #Attr.12; - let Inspect.343 : Str = "["; - let Inspect.324 : Str = CallByName Inspect.59 Inspect.153 Inspect.343; - let Inspect.325 : {List I64, {}, {}} = Struct {Inspect.149, Inspect.150, Inspect.151}; - let Inspect.320 : {Str, Int1} = CallByName Inspect.154 Inspect.324 Inspect.325; - let Inspect.321 : {} = Struct {}; - let Inspect.316 : Str = CallByName Inspect.163 Inspect.320; - let Inspect.317 : Str = "]"; - let Inspect.315 : Str = CallByName Inspect.59 Inspect.316 Inspect.317; - ret Inspect.315; - -procedure Inspect.154 (Inspect.155, #Attr.12): - let Inspect.151 : {} = StructAtIndex 2 #Attr.12; - let Inspect.150 : {} = StructAtIndex 1 #Attr.12; - let Inspect.149 : List I64 = StructAtIndex 0 #Attr.12; - let Inspect.342 : Int1 = CallByName Bool.1; - let Inspect.328 : {Str, Int1} = Struct {Inspect.155, Inspect.342}; - let Inspect.327 : {Str, Int1} = CallByName List.18 Inspect.149 Inspect.328 Inspect.151; - ret Inspect.327; +procedure Inspect.156 (Inspect.157, #Attr.12): + let Inspect.155 : {} = StructAtIndex 2 #Attr.12; + let Inspect.154 : {} = StructAtIndex 1 #Attr.12; + let Inspect.153 : List I64 = StructAtIndex 0 #Attr.12; + let Inspect.347 : Str = "["; + let Inspect.328 : Str = CallByName Inspect.63 Inspect.157 Inspect.347; + let Inspect.329 : {List I64, {}, {}} = Struct {Inspect.153, Inspect.154, Inspect.155}; + let Inspect.324 : {Str, Int1} = CallByName Inspect.158 Inspect.328 Inspect.329; + let Inspect.325 : {} = Struct {}; + let Inspect.320 : Str = CallByName Inspect.167 Inspect.324; + let Inspect.321 : Str = "]"; + let Inspect.319 : Str = CallByName Inspect.63 Inspect.320 Inspect.321; + ret Inspect.319; -procedure Inspect.156 (Inspect.330, Inspect.159, Inspect.151): - let Inspect.157 : Str = StructAtIndex 0 Inspect.330; - let Inspect.158 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.340 Inspect.160: - let Inspect.337 : I64 = CallByName #Derived.3 Inspect.159; - let Inspect.333 : Str = CallByName Inspect.31 Inspect.337 Inspect.160; - let Inspect.334 : {} = Struct {}; - let Inspect.332 : {Str, Int1} = CallByName Inspect.161 Inspect.333; - ret Inspect.332; +procedure Inspect.158 (Inspect.159, #Attr.12): + let Inspect.155 : {} = StructAtIndex 2 #Attr.12; + let Inspect.154 : {} = StructAtIndex 1 #Attr.12; + let Inspect.153 : List I64 = StructAtIndex 0 #Attr.12; + let Inspect.346 : Int1 = CallByName Bool.1; + let Inspect.332 : {Str, Int1} = Struct {Inspect.159, Inspect.346}; + let Inspect.331 : {Str, Int1} = CallByName List.18 Inspect.153 Inspect.332 Inspect.155; + ret Inspect.331; + +procedure Inspect.160 (Inspect.334, Inspect.163, Inspect.155): + let Inspect.161 : Str = StructAtIndex 0 Inspect.334; + let Inspect.162 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.344 Inspect.164: + let Inspect.341 : I64 = CallByName #Derived.3 Inspect.163; + let Inspect.337 : Str = CallByName Inspect.31 Inspect.341 Inspect.164; + let Inspect.338 : {} = Struct {}; + let Inspect.336 : {Str, Int1} = CallByName Inspect.165 Inspect.337; + ret Inspect.336; in - if Inspect.158 then - let Inspect.341 : Str = ", "; - let Inspect.339 : Str = CallByName Inspect.59 Inspect.157 Inspect.341; - jump Inspect.340 Inspect.339; + if Inspect.162 then + let Inspect.345 : Str = ", "; + let Inspect.343 : Str = CallByName Inspect.63 Inspect.161 Inspect.345; + jump Inspect.344 Inspect.343; else - jump Inspect.340 Inspect.157; + jump Inspect.344 Inspect.161; -procedure Inspect.161 (Inspect.162): - let Inspect.336 : Int1 = CallByName Bool.2; - let Inspect.335 : {Str, Int1} = Struct {Inspect.162, Inspect.336}; - ret Inspect.335; +procedure Inspect.165 (Inspect.166): + let Inspect.340 : Int1 = CallByName Bool.2; + let Inspect.339 : {Str, Int1} = Struct {Inspect.166, Inspect.340}; + ret Inspect.339; -procedure Inspect.163 (Inspect.322): - let Inspect.323 : Str = StructAtIndex 0 Inspect.322; - ret Inspect.323; - -procedure Inspect.274 (Inspect.275, Inspect.273): - let Inspect.349 : Str = CallByName Num.96 Inspect.273; - let Inspect.348 : Str = CallByName Inspect.59 Inspect.275 Inspect.349; - ret Inspect.348; +procedure Inspect.167 (Inspect.326): + let Inspect.327 : Str = StructAtIndex 0 Inspect.326; + ret Inspect.327; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.278 (Inspect.279, Inspect.277): + let Inspect.353 : Str = CallByName Num.96 Inspect.277; + let Inspect.352 : Str = CallByName Inspect.63 Inspect.279 Inspect.353; + ret Inspect.352; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.310 : Str = CallByName Inspect.152 Inspect.145 Inspect.299; - ret Inspect.310; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.338 : Str = CallByName Inspect.274 Inspect.145 Inspect.299; - ret Inspect.338; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.314 : Str = CallByName Inspect.156 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.342 : Str = CallByName Inspect.278 Inspect.149 Inspect.303; + ret Inspect.342; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.36 (Inspect.149, Inspect.150, Inspect.151): - let Inspect.312 : {List I64, {}, {}} = Struct {Inspect.149, Inspect.150, Inspect.151}; - let Inspect.311 : {List I64, {}, {}} = CallByName Inspect.30 Inspect.312; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; ret Inspect.311; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : List I64 = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.4 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.40 (Inspect.153, Inspect.154, Inspect.155): + let Inspect.316 : {List I64, {}, {}} = Struct {Inspect.153, Inspect.154, Inspect.155}; + let Inspect.315 : {List I64, {}, {}} = CallByName Inspect.30 Inspect.316; + ret Inspect.315; -procedure Inspect.53 (Inspect.273): - let Inspect.344 : I64 = CallByName Inspect.30 Inspect.273; - ret Inspect.344; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : List I64 = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.4 Inspect.308 Inspect.312; + ret Inspect.307; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.319 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.319; +procedure Inspect.57 (Inspect.277): + let Inspect.348 : I64 = CallByName Inspect.30 Inspect.277; + ret Inspect.348; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.323 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.323; + +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : I64 = CallByName List.66 List.163 List.166; - let List.168 : {Str, Int1} = CallByName Inspect.156 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22, #Derived_gen.23): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : I64 = CallByName List.66 List.166 List.169; + let List.171 : {Str, Int1} = CallByName Inspect.160 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23; + jump List.580 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22 #Derived_gen.23; procedure Num.22 (#Attr.2, #Attr.3): - let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.283; procedure Num.51 (#Attr.2, #Attr.3): - let Num.280 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.282; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.2 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt b/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt index 8c6b47f52c2..6c645e8a727 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_nested_record_string.txt @@ -7,7 +7,7 @@ procedure #Derived.2 (#Derived.3, #Derived.1): let #Derived_gen.8 : Str = CallByName #Derived.4 #Derived.1; let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; - let #Derived_gen.4 : List {Str, Str} = CallByName Inspect.41 #Derived_gen.5; + let #Derived_gen.4 : List {Str, Str} = CallByName Inspect.45 #Derived_gen.5; let #Derived_gen.3 : Str = CallByName Inspect.31 #Derived_gen.4 #Derived.3; ret #Derived_gen.3; @@ -17,10 +17,10 @@ procedure #Derived.4 (#Derived.5): procedure #Derived.6 (#Derived.7, #Derived.5): let #Derived_gen.17 : Str = "b"; - let #Derived_gen.18 : Str = CallByName Inspect.43 #Derived.5; + let #Derived_gen.18 : Str = CallByName Inspect.47 #Derived.5; let #Derived_gen.16 : {Str, Str} = Struct {#Derived_gen.17, #Derived_gen.18}; let #Derived_gen.15 : List {Str, Str} = Array [#Derived_gen.16]; - let #Derived_gen.14 : List {Str, Str} = CallByName Inspect.41 #Derived_gen.15; + let #Derived_gen.14 : List {Str, Str} = CallByName Inspect.45 #Derived_gen.15; let #Derived_gen.13 : Str = CallByName Inspect.31 #Derived_gen.14 #Derived.7; ret #Derived_gen.13; @@ -32,245 +32,245 @@ procedure Bool.2 (): let Bool.25 : Int1 = true; ret Bool.25; -procedure Inspect.225 (Inspect.226, Inspect.224): - let Inspect.348 : Str = "{"; - let Inspect.324 : Str = CallByName Inspect.59 Inspect.226 Inspect.348; - let Inspect.320 : {Str, Int1} = CallByName Inspect.227 Inspect.324 Inspect.224; - let Inspect.321 : {} = Struct {}; - let Inspect.316 : Str = CallByName Inspect.239 Inspect.320; - let Inspect.317 : Str = "}"; - let Inspect.315 : Str = CallByName Inspect.59 Inspect.316 Inspect.317; - ret Inspect.315; - -procedure Inspect.225 (Inspect.226, Inspect.224): - let Inspect.388 : Str = "{"; - let Inspect.364 : Str = CallByName Inspect.59 Inspect.226 Inspect.388; - let Inspect.360 : {Str, Int1} = CallByName Inspect.227 Inspect.364 Inspect.224; - let Inspect.361 : {} = Struct {}; - let Inspect.356 : Str = CallByName Inspect.239 Inspect.360; - let Inspect.357 : Str = "}"; - let Inspect.355 : Str = CallByName Inspect.59 Inspect.356 Inspect.357; - ret Inspect.355; - -procedure Inspect.227 (Inspect.228, Inspect.224): - let Inspect.347 : Int1 = CallByName Bool.1; - let Inspect.328 : {Str, Int1} = Struct {Inspect.228, Inspect.347}; - let Inspect.329 : {} = Struct {}; - let Inspect.327 : {Str, Int1} = CallByName List.18 Inspect.224 Inspect.328 Inspect.329; - ret Inspect.327; - -procedure Inspect.227 (Inspect.228, Inspect.224): - let Inspect.387 : Int1 = CallByName Bool.1; - let Inspect.368 : {Str, Int1} = Struct {Inspect.228, Inspect.387}; - let Inspect.369 : {} = Struct {}; - let Inspect.367 : {Str, Int1} = CallByName List.18 Inspect.224 Inspect.368 Inspect.369; - ret Inspect.367; +procedure Inspect.229 (Inspect.230, Inspect.228): + let Inspect.352 : Str = "{"; + let Inspect.328 : Str = CallByName Inspect.63 Inspect.230 Inspect.352; + let Inspect.324 : {Str, Int1} = CallByName Inspect.231 Inspect.328 Inspect.228; + let Inspect.325 : {} = Struct {}; + let Inspect.320 : Str = CallByName Inspect.243 Inspect.324; + let Inspect.321 : Str = "}"; + let Inspect.319 : Str = CallByName Inspect.63 Inspect.320 Inspect.321; + ret Inspect.319; + +procedure Inspect.229 (Inspect.230, Inspect.228): + let Inspect.392 : Str = "{"; + let Inspect.368 : Str = CallByName Inspect.63 Inspect.230 Inspect.392; + let Inspect.364 : {Str, Int1} = CallByName Inspect.231 Inspect.368 Inspect.228; + let Inspect.365 : {} = Struct {}; + let Inspect.360 : Str = CallByName Inspect.243 Inspect.364; + let Inspect.361 : Str = "}"; + let Inspect.359 : Str = CallByName Inspect.63 Inspect.360 Inspect.361; + ret Inspect.359; -procedure Inspect.229 (Inspect.330, Inspect.331): - let Inspect.232 : Str = StructAtIndex 0 Inspect.331; - let Inspect.233 : Str = StructAtIndex 1 Inspect.331; - let Inspect.230 : Str = StructAtIndex 0 Inspect.330; - let Inspect.231 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.345 Inspect.234: - let Inspect.342 : Str = CallByName Inspect.59 Inspect.234 Inspect.232; - let Inspect.343 : Str = ": "; - let Inspect.337 : Str = CallByName Inspect.59 Inspect.342 Inspect.343; - let Inspect.333 : Str = CallByName Inspect.235 Inspect.337 Inspect.233; - let Inspect.334 : {} = Struct {}; - let Inspect.332 : {Str, Int1} = CallByName Inspect.237 Inspect.333; - ret Inspect.332; +procedure Inspect.231 (Inspect.232, Inspect.228): + let Inspect.351 : Int1 = CallByName Bool.1; + let Inspect.332 : {Str, Int1} = Struct {Inspect.232, Inspect.351}; + let Inspect.333 : {} = Struct {}; + let Inspect.331 : {Str, Int1} = CallByName List.18 Inspect.228 Inspect.332 Inspect.333; + ret Inspect.331; + +procedure Inspect.231 (Inspect.232, Inspect.228): + let Inspect.391 : Int1 = CallByName Bool.1; + let Inspect.372 : {Str, Int1} = Struct {Inspect.232, Inspect.391}; + let Inspect.373 : {} = Struct {}; + let Inspect.371 : {Str, Int1} = CallByName List.18 Inspect.228 Inspect.372 Inspect.373; + ret Inspect.371; + +procedure Inspect.233 (Inspect.334, Inspect.335): + let Inspect.236 : Str = StructAtIndex 0 Inspect.335; + let Inspect.237 : Str = StructAtIndex 1 Inspect.335; + let Inspect.234 : Str = StructAtIndex 0 Inspect.334; + let Inspect.235 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.349 Inspect.238: + let Inspect.346 : Str = CallByName Inspect.63 Inspect.238 Inspect.236; + let Inspect.347 : Str = ": "; + let Inspect.341 : Str = CallByName Inspect.63 Inspect.346 Inspect.347; + let Inspect.337 : Str = CallByName Inspect.239 Inspect.341 Inspect.237; + let Inspect.338 : {} = Struct {}; + let Inspect.336 : {Str, Int1} = CallByName Inspect.241 Inspect.337; + ret Inspect.336; in - if Inspect.231 then - let Inspect.346 : Str = ", "; - let Inspect.344 : Str = CallByName Inspect.59 Inspect.230 Inspect.346; - jump Inspect.345 Inspect.344; + if Inspect.235 then + let Inspect.350 : Str = ", "; + let Inspect.348 : Str = CallByName Inspect.63 Inspect.234 Inspect.350; + jump Inspect.349 Inspect.348; else - jump Inspect.345 Inspect.230; - -procedure Inspect.229 (Inspect.330, Inspect.331): - let Inspect.232 : Str = StructAtIndex 0 Inspect.331; - let Inspect.233 : Str = StructAtIndex 1 Inspect.331; - let Inspect.230 : Str = StructAtIndex 0 Inspect.330; - let Inspect.231 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.385 Inspect.234: - let Inspect.382 : Str = CallByName Inspect.59 Inspect.234 Inspect.232; - let Inspect.383 : Str = ": "; - let Inspect.377 : Str = CallByName Inspect.59 Inspect.382 Inspect.383; - let Inspect.373 : Str = CallByName Inspect.235 Inspect.377 Inspect.233; - let Inspect.374 : {} = Struct {}; - let Inspect.372 : {Str, Int1} = CallByName Inspect.237 Inspect.373; - ret Inspect.372; + jump Inspect.349 Inspect.234; + +procedure Inspect.233 (Inspect.334, Inspect.335): + let Inspect.236 : Str = StructAtIndex 0 Inspect.335; + let Inspect.237 : Str = StructAtIndex 1 Inspect.335; + let Inspect.234 : Str = StructAtIndex 0 Inspect.334; + let Inspect.235 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.389 Inspect.238: + let Inspect.386 : Str = CallByName Inspect.63 Inspect.238 Inspect.236; + let Inspect.387 : Str = ": "; + let Inspect.381 : Str = CallByName Inspect.63 Inspect.386 Inspect.387; + let Inspect.377 : Str = CallByName Inspect.239 Inspect.381 Inspect.237; + let Inspect.378 : {} = Struct {}; + let Inspect.376 : {Str, Int1} = CallByName Inspect.241 Inspect.377; + ret Inspect.376; in - if Inspect.231 then - let Inspect.386 : Str = ", "; - let Inspect.384 : Str = CallByName Inspect.59 Inspect.230 Inspect.386; - jump Inspect.385 Inspect.384; + if Inspect.235 then + let Inspect.390 : Str = ", "; + let Inspect.388 : Str = CallByName Inspect.63 Inspect.234 Inspect.390; + jump Inspect.389 Inspect.388; else - jump Inspect.385 Inspect.230; - -procedure Inspect.235 (Inspect.236, Inspect.233): - let Inspect.340 : Str = CallByName Inspect.31 Inspect.233 Inspect.236; - ret Inspect.340; + jump Inspect.389 Inspect.234; -procedure Inspect.235 (Inspect.236, Inspect.233): - let Inspect.380 : Str = CallByName Inspect.31 Inspect.233 Inspect.236; - ret Inspect.380; +procedure Inspect.239 (Inspect.240, Inspect.237): + let Inspect.344 : Str = CallByName Inspect.31 Inspect.237 Inspect.240; + ret Inspect.344; -procedure Inspect.237 (Inspect.238): - let Inspect.376 : Int1 = CallByName Bool.2; - let Inspect.375 : {Str, Int1} = Struct {Inspect.238, Inspect.376}; - ret Inspect.375; +procedure Inspect.239 (Inspect.240, Inspect.237): + let Inspect.384 : Str = CallByName Inspect.31 Inspect.237 Inspect.240; + ret Inspect.384; -procedure Inspect.239 (Inspect.322): - let Inspect.363 : Str = StructAtIndex 0 Inspect.322; - ret Inspect.363; +procedure Inspect.241 (Inspect.242): + let Inspect.380 : Int1 = CallByName Bool.2; + let Inspect.379 : {Str, Int1} = Struct {Inspect.242, Inspect.380}; + ret Inspect.379; -procedure Inspect.246 (Inspect.247, Inspect.245): - let Inspect.397 : Str = "\""; - let Inspect.396 : Str = CallByName Inspect.59 Inspect.247 Inspect.397; - let Inspect.394 : Str = CallByName Inspect.59 Inspect.396 Inspect.245; - let Inspect.395 : Str = "\""; - let Inspect.393 : Str = CallByName Inspect.59 Inspect.394 Inspect.395; - ret Inspect.393; +procedure Inspect.243 (Inspect.326): + let Inspect.367 : Str = StructAtIndex 0 Inspect.326; + ret Inspect.367; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.401 : Str = "\""; + let Inspect.400 : Str = CallByName Inspect.63 Inspect.251 Inspect.401; + let Inspect.398 : Str = CallByName Inspect.63 Inspect.400 Inspect.249; + let Inspect.399 : Str = "\""; + let Inspect.397 : Str = CallByName Inspect.63 Inspect.398 Inspect.399; + ret Inspect.397; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.310 : Str = CallByName Inspect.225 Inspect.145 Inspect.299; - ret Inspect.310; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.341 : Str = CallByName #Derived.6 Inspect.145 Inspect.299; - ret Inspect.341; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.314 : Str = CallByName Inspect.229 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.350 : Str = CallByName Inspect.225 Inspect.145 Inspect.299; - ret Inspect.350; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.345 : Str = CallByName #Derived.6 Inspect.149 Inspect.303; + ret Inspect.345; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.381 : Str = CallByName Inspect.246 Inspect.145 Inspect.299; - ret Inspect.381; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.354 : Str = CallByName Inspect.229 Inspect.149 Inspect.303; + ret Inspect.354; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.385 : Str = CallByName Inspect.250 Inspect.149 Inspect.303; + ret Inspect.385; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.41 (Inspect.224): - let Inspect.311 : List {Str, Str} = CallByName Inspect.30 Inspect.224; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; ret Inspect.311; -procedure Inspect.41 (Inspect.224): - let Inspect.351 : List {Str, Str} = CallByName Inspect.30 Inspect.224; - ret Inspect.351; +procedure Inspect.45 (Inspect.228): + let Inspect.315 : List {Str, Str} = CallByName Inspect.30 Inspect.228; + ret Inspect.315; -procedure Inspect.43 (Inspect.245): - let Inspect.389 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.389; +procedure Inspect.45 (Inspect.228): + let Inspect.355 : List {Str, Str} = CallByName Inspect.30 Inspect.228; + ret Inspect.355; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : Str = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.2 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.47 (Inspect.249): + let Inspect.393 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.393; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.359 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.359; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : Str = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.2 Inspect.308 Inspect.312; + ret Inspect.307; + +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.363 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.363; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; -procedure List.18 (List.160, List.161, List.162): - let List.587 : U64 = 0i64; - let List.588 : U64 = CallByName List.6 List.160; - let List.586 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.587 List.588; - ret List.586; +procedure List.18 (List.163, List.164, List.165): + let List.590 : U64 = 0i64; + let List.591 : U64 = CallByName List.6 List.163; + let List.589 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.590 List.591; + ret List.589; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.6 (#Attr.2): - let List.597 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.597; + let List.600 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.600; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.66 (#Attr.2, #Attr.3): - let List.596 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.596; - -procedure List.92 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): - joinpoint List.589 List.163 List.164 List.165 List.166 List.167: - let List.591 : Int1 = CallByName Num.22 List.166 List.167; - if List.591 then - let List.595 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.595; - let List.168 : {Str, Int1} = CallByName Inspect.229 List.164 List.595; - let List.594 : U64 = 1i64; - let List.593 : U64 = CallByName Num.51 List.166 List.594; - jump List.589 List.163 List.168 List.165 List.593 List.167; + let List.599 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.599; + +procedure List.95 (#Derived_gen.22, #Derived_gen.23, #Derived_gen.24, #Derived_gen.25, #Derived_gen.26): + joinpoint List.592 List.166 List.167 List.168 List.169 List.170: + let List.594 : Int1 = CallByName Num.22 List.169 List.170; + if List.594 then + let List.598 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.598; + let List.171 : {Str, Int1} = CallByName Inspect.233 List.167 List.598; + let List.597 : U64 = 1i64; + let List.596 : U64 = CallByName Num.51 List.169 List.597; + jump List.592 List.166 List.171 List.168 List.596 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.589 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; - -procedure List.92 (#Derived_gen.39, #Derived_gen.40, #Derived_gen.41, #Derived_gen.42, #Derived_gen.43): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : {Str, Int1} = CallByName Inspect.229 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + jump List.592 #Derived_gen.22 #Derived_gen.23 #Derived_gen.24 #Derived_gen.25 #Derived_gen.26; + +procedure List.95 (#Derived_gen.43, #Derived_gen.44, #Derived_gen.45, #Derived_gen.46, #Derived_gen.47): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : {Str, Int1} = CallByName Inspect.233 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.39 #Derived_gen.40 #Derived_gen.41 #Derived_gen.42 #Derived_gen.43; + jump List.580 #Derived_gen.43 #Derived_gen.44 #Derived_gen.45 #Derived_gen.46 #Derived_gen.47; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Str.3 (#Attr.2, #Attr.3): - let Str.233 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.233; + let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.237; procedure Test.0 (): let Test.4 : Str = "bar"; diff --git a/crates/compiler/test_mono/generated/inspect_derived_record.txt b/crates/compiler/test_mono/generated/inspect_derived_record.txt index d5610c0aee8..2b45cdc1cd2 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_record.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_record.txt @@ -4,15 +4,15 @@ procedure #Derived.0 (#Derived.1): procedure #Derived.2 (#Derived.3, #Derived.1): let #Derived_gen.13 : I64 = StructAtIndex 1 #Derived.1; - let #Derived_gen.11 : [C I64, C Decimal] = CallByName Inspect.53 #Derived_gen.13; + let #Derived_gen.11 : [C I64, C Decimal] = CallByName Inspect.57 #Derived_gen.13; let #Derived_gen.12 : Str = "a"; let #Derived_gen.6 : {[C I64, C Decimal], Str} = Struct {#Derived_gen.11, #Derived_gen.12}; let #Derived_gen.10 : Decimal = StructAtIndex 0 #Derived.1; - let #Derived_gen.8 : [C I64, C Decimal] = CallByName Inspect.58 #Derived_gen.10; + let #Derived_gen.8 : [C I64, C Decimal] = CallByName Inspect.62 #Derived_gen.10; let #Derived_gen.9 : Str = "b"; let #Derived_gen.7 : {[C I64, C Decimal], Str} = Struct {#Derived_gen.8, #Derived_gen.9}; let #Derived_gen.5 : List {[C I64, C Decimal], Str} = Array [#Derived_gen.6, #Derived_gen.7]; - let #Derived_gen.4 : List {[C I64, C Decimal], Str} = CallByName Inspect.41 #Derived_gen.5; + let #Derived_gen.4 : List {[C I64, C Decimal], Str} = CallByName Inspect.45 #Derived_gen.5; let #Derived_gen.3 : Str = CallByName Inspect.31 #Derived_gen.4 #Derived.3; ret #Derived_gen.3; @@ -24,181 +24,181 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Inspect.225 (Inspect.226, Inspect.224): - let Inspect.349 : Str = "{"; - let Inspect.324 : Str = CallByName Inspect.59 Inspect.226 Inspect.349; - let Inspect.320 : {Str, Int1} = CallByName Inspect.227 Inspect.324 Inspect.224; - let Inspect.321 : {} = Struct {}; - let Inspect.316 : Str = CallByName Inspect.239 Inspect.320; - let Inspect.317 : Str = "}"; - let Inspect.315 : Str = CallByName Inspect.59 Inspect.316 Inspect.317; - ret Inspect.315; - -procedure Inspect.227 (Inspect.228, Inspect.224): - let Inspect.348 : Int1 = CallByName Bool.1; - let Inspect.328 : {Str, Int1} = Struct {Inspect.228, Inspect.348}; - let Inspect.329 : {} = Struct {}; - let Inspect.327 : {Str, Int1} = CallByName List.18 Inspect.224 Inspect.328 Inspect.329; - ret Inspect.327; +procedure Inspect.229 (Inspect.230, Inspect.228): + let Inspect.353 : Str = "{"; + let Inspect.328 : Str = CallByName Inspect.63 Inspect.230 Inspect.353; + let Inspect.324 : {Str, Int1} = CallByName Inspect.231 Inspect.328 Inspect.228; + let Inspect.325 : {} = Struct {}; + let Inspect.320 : Str = CallByName Inspect.243 Inspect.324; + let Inspect.321 : Str = "}"; + let Inspect.319 : Str = CallByName Inspect.63 Inspect.320 Inspect.321; + ret Inspect.319; -procedure Inspect.229 (Inspect.330, Inspect.331): - let Inspect.233 : [C I64, C Decimal] = StructAtIndex 0 Inspect.331; - let Inspect.232 : Str = StructAtIndex 1 Inspect.331; - let Inspect.230 : Str = StructAtIndex 0 Inspect.330; - let Inspect.231 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.346 Inspect.234: - let Inspect.343 : Str = CallByName Inspect.59 Inspect.234 Inspect.232; - let Inspect.344 : Str = ": "; - let Inspect.337 : Str = CallByName Inspect.59 Inspect.343 Inspect.344; - let Inspect.333 : Str = CallByName Inspect.235 Inspect.337 Inspect.233; - let Inspect.334 : {} = Struct {}; - let Inspect.332 : {Str, Int1} = CallByName Inspect.237 Inspect.333; - ret Inspect.332; +procedure Inspect.231 (Inspect.232, Inspect.228): + let Inspect.352 : Int1 = CallByName Bool.1; + let Inspect.332 : {Str, Int1} = Struct {Inspect.232, Inspect.352}; + let Inspect.333 : {} = Struct {}; + let Inspect.331 : {Str, Int1} = CallByName List.18 Inspect.228 Inspect.332 Inspect.333; + ret Inspect.331; + +procedure Inspect.233 (Inspect.334, Inspect.335): + let Inspect.237 : [C I64, C Decimal] = StructAtIndex 0 Inspect.335; + let Inspect.236 : Str = StructAtIndex 1 Inspect.335; + let Inspect.234 : Str = StructAtIndex 0 Inspect.334; + let Inspect.235 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.350 Inspect.238: + let Inspect.347 : Str = CallByName Inspect.63 Inspect.238 Inspect.236; + let Inspect.348 : Str = ": "; + let Inspect.341 : Str = CallByName Inspect.63 Inspect.347 Inspect.348; + let Inspect.337 : Str = CallByName Inspect.239 Inspect.341 Inspect.237; + let Inspect.338 : {} = Struct {}; + let Inspect.336 : {Str, Int1} = CallByName Inspect.241 Inspect.337; + ret Inspect.336; in - if Inspect.231 then - let Inspect.347 : Str = ", "; - let Inspect.345 : Str = CallByName Inspect.59 Inspect.230 Inspect.347; - jump Inspect.346 Inspect.345; + if Inspect.235 then + let Inspect.351 : Str = ", "; + let Inspect.349 : Str = CallByName Inspect.63 Inspect.234 Inspect.351; + jump Inspect.350 Inspect.349; else - jump Inspect.346 Inspect.230; + jump Inspect.350 Inspect.234; -procedure Inspect.235 (Inspect.236, Inspect.233): - let Inspect.340 : Str = CallByName Inspect.31 Inspect.233 Inspect.236; - ret Inspect.340; +procedure Inspect.239 (Inspect.240, Inspect.237): + let Inspect.344 : Str = CallByName Inspect.31 Inspect.237 Inspect.240; + ret Inspect.344; -procedure Inspect.237 (Inspect.238): - let Inspect.336 : Int1 = CallByName Bool.2; - let Inspect.335 : {Str, Int1} = Struct {Inspect.238, Inspect.336}; - ret Inspect.335; +procedure Inspect.241 (Inspect.242): + let Inspect.340 : Int1 = CallByName Bool.2; + let Inspect.339 : {Str, Int1} = Struct {Inspect.242, Inspect.340}; + ret Inspect.339; -procedure Inspect.239 (Inspect.322): - let Inspect.323 : Str = StructAtIndex 0 Inspect.322; - ret Inspect.323; +procedure Inspect.243 (Inspect.326): + let Inspect.327 : Str = StructAtIndex 0 Inspect.326; + ret Inspect.327; -procedure Inspect.274 (Inspect.275, #Attr.12): - let Inspect.362 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Inspect.361 : Str = CallByName Num.96 Inspect.362; - let Inspect.360 : Str = CallByName Inspect.59 Inspect.275 Inspect.361; - ret Inspect.360; +procedure Inspect.278 (Inspect.279, #Attr.12): + let Inspect.366 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Inspect.365 : Str = CallByName Num.96 Inspect.366; + let Inspect.364 : Str = CallByName Inspect.63 Inspect.279 Inspect.365; + ret Inspect.364; -procedure Inspect.289 (Inspect.290, #Attr.12): - let Inspect.356 : Decimal = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Inspect.355 : Str = CallByName Num.96 Inspect.356; - let Inspect.354 : Str = CallByName Inspect.59 Inspect.290 Inspect.355; - ret Inspect.354; +procedure Inspect.293 (Inspect.294, #Attr.12): + let Inspect.360 : Decimal = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Inspect.359 : Str = CallByName Num.96 Inspect.360; + let Inspect.358 : Str = CallByName Inspect.63 Inspect.294 Inspect.359; + ret Inspect.358; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.310 : Str = CallByName Inspect.225 Inspect.145 Inspect.299; - ret Inspect.310; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.314 : Str = CallByName Inspect.229 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.342 : U8 = GetTagId Inspect.299; - switch Inspect.342: +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.346 : U8 = GetTagId Inspect.303; + switch Inspect.346: case 0: - let Inspect.341 : Str = CallByName Inspect.274 Inspect.145 Inspect.299; - ret Inspect.341; + let Inspect.345 : Str = CallByName Inspect.278 Inspect.149 Inspect.303; + ret Inspect.345; default: - let Inspect.341 : Str = CallByName Inspect.289 Inspect.145 Inspect.299; - ret Inspect.341; + let Inspect.345 : Str = CallByName Inspect.293 Inspect.149 Inspect.303; + ret Inspect.345; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; + +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; + +procedure Inspect.45 (Inspect.228): + let Inspect.315 : List {[C I64, C Decimal], Str} = CallByName Inspect.30 Inspect.228; + ret Inspect.315; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : {Decimal, I64} = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.2 Inspect.308 Inspect.312; ret Inspect.307; -procedure Inspect.41 (Inspect.224): - let Inspect.311 : List {[C I64, C Decimal], Str} = CallByName Inspect.30 Inspect.224; - ret Inspect.311; +procedure Inspect.57 (Inspect.277): + let Inspect.362 : [C I64, C Decimal] = TagId(0) Inspect.277; + let Inspect.361 : [C I64, C Decimal] = CallByName Inspect.30 Inspect.362; + ret Inspect.361; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : {Decimal, I64} = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.2 Inspect.304 Inspect.308; - ret Inspect.303; - -procedure Inspect.53 (Inspect.273): - let Inspect.358 : [C I64, C Decimal] = TagId(0) Inspect.273; - let Inspect.357 : [C I64, C Decimal] = CallByName Inspect.30 Inspect.358; - ret Inspect.357; - -procedure Inspect.58 (Inspect.288): - let Inspect.351 : [C I64, C Decimal] = TagId(1) Inspect.288; - let Inspect.350 : [C I64, C Decimal] = CallByName Inspect.30 Inspect.351; - ret Inspect.350; - -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.319 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.319; +procedure Inspect.62 (Inspect.292): + let Inspect.355 : [C I64, C Decimal] = TagId(1) Inspect.292; + let Inspect.354 : [C I64, C Decimal] = CallByName Inspect.30 Inspect.355; + ret Inspect.354; + +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.323 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.323; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {[C I64, C Decimal], Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {[C I64, C Decimal], Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : {Str, Int1} = CallByName Inspect.229 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : {[C I64, C Decimal], Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {[C I64, C Decimal], Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : {Str, Int1} = CallByName Inspect.233 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; + jump List.580 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Num.96 (#Attr.2): - let Num.280 : Str = lowlevel NumToStr #Attr.2; - ret Num.280; + let Num.282 : Str = lowlevel NumToStr #Attr.2; + ret Num.282; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.3 : Decimal = 3dec; diff --git a/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt b/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt index 1c9dddfb57a..80decd31e50 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_record_one_field_string.txt @@ -4,10 +4,10 @@ procedure #Derived.0 (#Derived.1): procedure #Derived.2 (#Derived.3, #Derived.1): let #Derived_gen.7 : Str = "a"; - let #Derived_gen.8 : Str = CallByName Inspect.43 #Derived.1; + let #Derived_gen.8 : Str = CallByName Inspect.47 #Derived.1; let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.7, #Derived_gen.8}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6]; - let #Derived_gen.4 : List {Str, Str} = CallByName Inspect.41 #Derived_gen.5; + let #Derived_gen.4 : List {Str, Str} = CallByName Inspect.45 #Derived_gen.5; let #Derived_gen.3 : Str = CallByName Inspect.31 #Derived_gen.4 #Derived.3; ret #Derived_gen.3; @@ -19,155 +19,155 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Inspect.225 (Inspect.226, Inspect.224): - let Inspect.348 : Str = "{"; - let Inspect.324 : Str = CallByName Inspect.59 Inspect.226 Inspect.348; - let Inspect.320 : {Str, Int1} = CallByName Inspect.227 Inspect.324 Inspect.224; - let Inspect.321 : {} = Struct {}; - let Inspect.316 : Str = CallByName Inspect.239 Inspect.320; - let Inspect.317 : Str = "}"; - let Inspect.315 : Str = CallByName Inspect.59 Inspect.316 Inspect.317; - ret Inspect.315; - -procedure Inspect.227 (Inspect.228, Inspect.224): - let Inspect.347 : Int1 = CallByName Bool.1; - let Inspect.328 : {Str, Int1} = Struct {Inspect.228, Inspect.347}; - let Inspect.329 : {} = Struct {}; - let Inspect.327 : {Str, Int1} = CallByName List.18 Inspect.224 Inspect.328 Inspect.329; - ret Inspect.327; +procedure Inspect.229 (Inspect.230, Inspect.228): + let Inspect.352 : Str = "{"; + let Inspect.328 : Str = CallByName Inspect.63 Inspect.230 Inspect.352; + let Inspect.324 : {Str, Int1} = CallByName Inspect.231 Inspect.328 Inspect.228; + let Inspect.325 : {} = Struct {}; + let Inspect.320 : Str = CallByName Inspect.243 Inspect.324; + let Inspect.321 : Str = "}"; + let Inspect.319 : Str = CallByName Inspect.63 Inspect.320 Inspect.321; + ret Inspect.319; -procedure Inspect.229 (Inspect.330, Inspect.331): - let Inspect.232 : Str = StructAtIndex 0 Inspect.331; - let Inspect.233 : Str = StructAtIndex 1 Inspect.331; - let Inspect.230 : Str = StructAtIndex 0 Inspect.330; - let Inspect.231 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.345 Inspect.234: - let Inspect.342 : Str = CallByName Inspect.59 Inspect.234 Inspect.232; - let Inspect.343 : Str = ": "; - let Inspect.337 : Str = CallByName Inspect.59 Inspect.342 Inspect.343; - let Inspect.333 : Str = CallByName Inspect.235 Inspect.337 Inspect.233; - let Inspect.334 : {} = Struct {}; - let Inspect.332 : {Str, Int1} = CallByName Inspect.237 Inspect.333; - ret Inspect.332; +procedure Inspect.231 (Inspect.232, Inspect.228): + let Inspect.351 : Int1 = CallByName Bool.1; + let Inspect.332 : {Str, Int1} = Struct {Inspect.232, Inspect.351}; + let Inspect.333 : {} = Struct {}; + let Inspect.331 : {Str, Int1} = CallByName List.18 Inspect.228 Inspect.332 Inspect.333; + ret Inspect.331; + +procedure Inspect.233 (Inspect.334, Inspect.335): + let Inspect.236 : Str = StructAtIndex 0 Inspect.335; + let Inspect.237 : Str = StructAtIndex 1 Inspect.335; + let Inspect.234 : Str = StructAtIndex 0 Inspect.334; + let Inspect.235 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.349 Inspect.238: + let Inspect.346 : Str = CallByName Inspect.63 Inspect.238 Inspect.236; + let Inspect.347 : Str = ": "; + let Inspect.341 : Str = CallByName Inspect.63 Inspect.346 Inspect.347; + let Inspect.337 : Str = CallByName Inspect.239 Inspect.341 Inspect.237; + let Inspect.338 : {} = Struct {}; + let Inspect.336 : {Str, Int1} = CallByName Inspect.241 Inspect.337; + ret Inspect.336; in - if Inspect.231 then - let Inspect.346 : Str = ", "; - let Inspect.344 : Str = CallByName Inspect.59 Inspect.230 Inspect.346; - jump Inspect.345 Inspect.344; + if Inspect.235 then + let Inspect.350 : Str = ", "; + let Inspect.348 : Str = CallByName Inspect.63 Inspect.234 Inspect.350; + jump Inspect.349 Inspect.348; else - jump Inspect.345 Inspect.230; + jump Inspect.349 Inspect.234; -procedure Inspect.235 (Inspect.236, Inspect.233): - let Inspect.340 : Str = CallByName Inspect.31 Inspect.233 Inspect.236; - ret Inspect.340; +procedure Inspect.239 (Inspect.240, Inspect.237): + let Inspect.344 : Str = CallByName Inspect.31 Inspect.237 Inspect.240; + ret Inspect.344; -procedure Inspect.237 (Inspect.238): - let Inspect.336 : Int1 = CallByName Bool.2; - let Inspect.335 : {Str, Int1} = Struct {Inspect.238, Inspect.336}; - ret Inspect.335; +procedure Inspect.241 (Inspect.242): + let Inspect.340 : Int1 = CallByName Bool.2; + let Inspect.339 : {Str, Int1} = Struct {Inspect.242, Inspect.340}; + ret Inspect.339; -procedure Inspect.239 (Inspect.322): - let Inspect.323 : Str = StructAtIndex 0 Inspect.322; - ret Inspect.323; +procedure Inspect.243 (Inspect.326): + let Inspect.327 : Str = StructAtIndex 0 Inspect.326; + ret Inspect.327; -procedure Inspect.246 (Inspect.247, Inspect.245): - let Inspect.357 : Str = "\""; - let Inspect.356 : Str = CallByName Inspect.59 Inspect.247 Inspect.357; - let Inspect.354 : Str = CallByName Inspect.59 Inspect.356 Inspect.245; - let Inspect.355 : Str = "\""; - let Inspect.353 : Str = CallByName Inspect.59 Inspect.354 Inspect.355; - ret Inspect.353; +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.361 : Str = "\""; + let Inspect.360 : Str = CallByName Inspect.63 Inspect.251 Inspect.361; + let Inspect.358 : Str = CallByName Inspect.63 Inspect.360 Inspect.249; + let Inspect.359 : Str = "\""; + let Inspect.357 : Str = CallByName Inspect.63 Inspect.358 Inspect.359; + ret Inspect.357; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.310 : Str = CallByName Inspect.225 Inspect.145 Inspect.299; - ret Inspect.310; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.314 : Str = CallByName Inspect.229 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.341 : Str = CallByName Inspect.246 Inspect.145 Inspect.299; - ret Inspect.341; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.345 : Str = CallByName Inspect.250 Inspect.149 Inspect.303; + ret Inspect.345; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; - -procedure Inspect.41 (Inspect.224): - let Inspect.311 : List {Str, Str} = CallByName Inspect.30 Inspect.224; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; ret Inspect.311; -procedure Inspect.43 (Inspect.245): - let Inspect.349 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.349; +procedure Inspect.45 (Inspect.228): + let Inspect.315 : List {Str, Str} = CallByName Inspect.30 Inspect.228; + ret Inspect.315; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : Str = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.2 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.47 (Inspect.249): + let Inspect.353 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.353; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.319 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.319; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : Str = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.2 Inspect.308 Inspect.312; + ret Inspect.307; + +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.323 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.323; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : {Str, Int1} = CallByName Inspect.229 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : {Str, Int1} = CallByName Inspect.233 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14; + jump List.580 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.3 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt b/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt index 482b26ad63c..dbd0ab7df0a 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_record_two_field_strings.txt @@ -6,15 +6,15 @@ procedure #Derived.2 (#Derived.3, #Derived.1): let #Derived_gen.11 : Str = "a"; let #Derived_gen.13 : Str = StructAtIndex 0 #Derived.1; inc #Derived_gen.13; - let #Derived_gen.12 : Str = CallByName Inspect.43 #Derived_gen.13; + let #Derived_gen.12 : Str = CallByName Inspect.47 #Derived_gen.13; let #Derived_gen.6 : {Str, Str} = Struct {#Derived_gen.11, #Derived_gen.12}; let #Derived_gen.8 : Str = "b"; let #Derived_gen.10 : Str = StructAtIndex 1 #Derived.1; dec #Derived_gen.13; - let #Derived_gen.9 : Str = CallByName Inspect.43 #Derived_gen.10; + let #Derived_gen.9 : Str = CallByName Inspect.47 #Derived_gen.10; let #Derived_gen.7 : {Str, Str} = Struct {#Derived_gen.8, #Derived_gen.9}; let #Derived_gen.5 : List {Str, Str} = Array [#Derived_gen.6, #Derived_gen.7]; - let #Derived_gen.4 : List {Str, Str} = CallByName Inspect.41 #Derived_gen.5; + let #Derived_gen.4 : List {Str, Str} = CallByName Inspect.45 #Derived_gen.5; let #Derived_gen.3 : Str = CallByName Inspect.31 #Derived_gen.4 #Derived.3; ret #Derived_gen.3; @@ -26,155 +26,155 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Inspect.225 (Inspect.226, Inspect.224): - let Inspect.348 : Str = "{"; - let Inspect.324 : Str = CallByName Inspect.59 Inspect.226 Inspect.348; - let Inspect.320 : {Str, Int1} = CallByName Inspect.227 Inspect.324 Inspect.224; - let Inspect.321 : {} = Struct {}; - let Inspect.316 : Str = CallByName Inspect.239 Inspect.320; - let Inspect.317 : Str = "}"; - let Inspect.315 : Str = CallByName Inspect.59 Inspect.316 Inspect.317; - ret Inspect.315; - -procedure Inspect.227 (Inspect.228, Inspect.224): - let Inspect.347 : Int1 = CallByName Bool.1; - let Inspect.328 : {Str, Int1} = Struct {Inspect.228, Inspect.347}; - let Inspect.329 : {} = Struct {}; - let Inspect.327 : {Str, Int1} = CallByName List.18 Inspect.224 Inspect.328 Inspect.329; - ret Inspect.327; +procedure Inspect.229 (Inspect.230, Inspect.228): + let Inspect.352 : Str = "{"; + let Inspect.328 : Str = CallByName Inspect.63 Inspect.230 Inspect.352; + let Inspect.324 : {Str, Int1} = CallByName Inspect.231 Inspect.328 Inspect.228; + let Inspect.325 : {} = Struct {}; + let Inspect.320 : Str = CallByName Inspect.243 Inspect.324; + let Inspect.321 : Str = "}"; + let Inspect.319 : Str = CallByName Inspect.63 Inspect.320 Inspect.321; + ret Inspect.319; -procedure Inspect.229 (Inspect.330, Inspect.331): - let Inspect.232 : Str = StructAtIndex 0 Inspect.331; - let Inspect.233 : Str = StructAtIndex 1 Inspect.331; - let Inspect.230 : Str = StructAtIndex 0 Inspect.330; - let Inspect.231 : Int1 = StructAtIndex 1 Inspect.330; - joinpoint Inspect.345 Inspect.234: - let Inspect.342 : Str = CallByName Inspect.59 Inspect.234 Inspect.232; - let Inspect.343 : Str = ": "; - let Inspect.337 : Str = CallByName Inspect.59 Inspect.342 Inspect.343; - let Inspect.333 : Str = CallByName Inspect.235 Inspect.337 Inspect.233; - let Inspect.334 : {} = Struct {}; - let Inspect.332 : {Str, Int1} = CallByName Inspect.237 Inspect.333; - ret Inspect.332; +procedure Inspect.231 (Inspect.232, Inspect.228): + let Inspect.351 : Int1 = CallByName Bool.1; + let Inspect.332 : {Str, Int1} = Struct {Inspect.232, Inspect.351}; + let Inspect.333 : {} = Struct {}; + let Inspect.331 : {Str, Int1} = CallByName List.18 Inspect.228 Inspect.332 Inspect.333; + ret Inspect.331; + +procedure Inspect.233 (Inspect.334, Inspect.335): + let Inspect.236 : Str = StructAtIndex 0 Inspect.335; + let Inspect.237 : Str = StructAtIndex 1 Inspect.335; + let Inspect.234 : Str = StructAtIndex 0 Inspect.334; + let Inspect.235 : Int1 = StructAtIndex 1 Inspect.334; + joinpoint Inspect.349 Inspect.238: + let Inspect.346 : Str = CallByName Inspect.63 Inspect.238 Inspect.236; + let Inspect.347 : Str = ": "; + let Inspect.341 : Str = CallByName Inspect.63 Inspect.346 Inspect.347; + let Inspect.337 : Str = CallByName Inspect.239 Inspect.341 Inspect.237; + let Inspect.338 : {} = Struct {}; + let Inspect.336 : {Str, Int1} = CallByName Inspect.241 Inspect.337; + ret Inspect.336; in - if Inspect.231 then - let Inspect.346 : Str = ", "; - let Inspect.344 : Str = CallByName Inspect.59 Inspect.230 Inspect.346; - jump Inspect.345 Inspect.344; + if Inspect.235 then + let Inspect.350 : Str = ", "; + let Inspect.348 : Str = CallByName Inspect.63 Inspect.234 Inspect.350; + jump Inspect.349 Inspect.348; else - jump Inspect.345 Inspect.230; + jump Inspect.349 Inspect.234; -procedure Inspect.235 (Inspect.236, Inspect.233): - let Inspect.340 : Str = CallByName Inspect.31 Inspect.233 Inspect.236; - ret Inspect.340; +procedure Inspect.239 (Inspect.240, Inspect.237): + let Inspect.344 : Str = CallByName Inspect.31 Inspect.237 Inspect.240; + ret Inspect.344; -procedure Inspect.237 (Inspect.238): - let Inspect.336 : Int1 = CallByName Bool.2; - let Inspect.335 : {Str, Int1} = Struct {Inspect.238, Inspect.336}; - ret Inspect.335; +procedure Inspect.241 (Inspect.242): + let Inspect.340 : Int1 = CallByName Bool.2; + let Inspect.339 : {Str, Int1} = Struct {Inspect.242, Inspect.340}; + ret Inspect.339; -procedure Inspect.239 (Inspect.322): - let Inspect.323 : Str = StructAtIndex 0 Inspect.322; - ret Inspect.323; +procedure Inspect.243 (Inspect.326): + let Inspect.327 : Str = StructAtIndex 0 Inspect.326; + ret Inspect.327; -procedure Inspect.246 (Inspect.247, Inspect.245): - let Inspect.357 : Str = "\""; - let Inspect.356 : Str = CallByName Inspect.59 Inspect.247 Inspect.357; - let Inspect.354 : Str = CallByName Inspect.59 Inspect.356 Inspect.245; - let Inspect.355 : Str = "\""; - let Inspect.353 : Str = CallByName Inspect.59 Inspect.354 Inspect.355; - ret Inspect.353; +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.361 : Str = "\""; + let Inspect.360 : Str = CallByName Inspect.63 Inspect.251 Inspect.361; + let Inspect.358 : Str = CallByName Inspect.63 Inspect.360 Inspect.249; + let Inspect.359 : Str = "\""; + let Inspect.357 : Str = CallByName Inspect.63 Inspect.358 Inspect.359; + ret Inspect.357; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.310 : Str = CallByName Inspect.225 Inspect.145 Inspect.299; - ret Inspect.310; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.314 : Str = CallByName Inspect.229 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.341 : Str = CallByName Inspect.246 Inspect.145 Inspect.299; - ret Inspect.341; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.345 : Str = CallByName Inspect.250 Inspect.149 Inspect.303; + ret Inspect.345; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; - -procedure Inspect.41 (Inspect.224): - let Inspect.311 : List {Str, Str} = CallByName Inspect.30 Inspect.224; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; ret Inspect.311; -procedure Inspect.43 (Inspect.245): - let Inspect.358 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.358; +procedure Inspect.45 (Inspect.228): + let Inspect.315 : List {Str, Str} = CallByName Inspect.30 Inspect.228; + ret Inspect.315; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : {Str, Str} = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.2 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.47 (Inspect.249): + let Inspect.362 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.362; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.319 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.319; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : {Str, Str} = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.2 Inspect.308 Inspect.312; + ret Inspect.307; + +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.323 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.323; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : {Str, Int1} = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : {Str, Int1} = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : {Str, Str} = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : {Str, Int1} = CallByName Inspect.229 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : {Str, Str} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.18, #Derived_gen.19, #Derived_gen.20, #Derived_gen.21, #Derived_gen.22): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : {Str, Str} = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : {Str, Int1} = CallByName Inspect.233 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; + jump List.580 #Derived_gen.18 #Derived_gen.19 #Derived_gen.20 #Derived_gen.21 #Derived_gen.22; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.3 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/inspect_derived_string.txt b/crates/compiler/test_mono/generated/inspect_derived_string.txt index 608567f910f..fa91df230bc 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_string.txt @@ -1,45 +1,45 @@ -procedure Inspect.246 (Inspect.247, Inspect.245): +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.323 : Str = "\""; + let Inspect.322 : Str = CallByName Inspect.63 Inspect.251 Inspect.323; + let Inspect.318 : Str = CallByName Inspect.63 Inspect.322 Inspect.249; let Inspect.319 : Str = "\""; - let Inspect.318 : Str = CallByName Inspect.59 Inspect.247 Inspect.319; - let Inspect.314 : Str = CallByName Inspect.59 Inspect.318 Inspect.245; - let Inspect.315 : Str = "\""; - let Inspect.313 : Str = CallByName Inspect.59 Inspect.314 Inspect.315; - ret Inspect.313; + let Inspect.317 : Str = CallByName Inspect.63 Inspect.318 Inspect.319; + ret Inspect.317; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; -procedure Inspect.43 (Inspect.245): - let Inspect.309 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.309; +procedure Inspect.47 (Inspect.249): + let Inspect.313 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.313; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : Str = CallByName Inspect.43 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName Inspect.246 Inspect.304 Inspect.308; - ret Inspect.303; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : Str = CallByName Inspect.47 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName Inspect.250 Inspect.308 Inspect.312; + ret Inspect.307; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.317 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.317; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.321 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.321; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.2 : Str = "abc"; diff --git a/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt b/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt index 1bbebf7cfd2..49f0629f96b 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_tag_one_field_string.txt @@ -8,168 +8,168 @@ procedure #Derived.3 (#Derived.4, #Derived.1): ret #Derived_gen.3; in let #Derived_gen.7 : Str = "A"; - let #Derived_gen.9 : Str = CallByName Inspect.43 #Derived.1; + let #Derived_gen.9 : Str = CallByName Inspect.47 #Derived.1; let #Derived_gen.8 : List Str = Array [#Derived_gen.9]; - let #Derived_gen.6 : [C Str, C Str List Str] = CallByName Inspect.39 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : [C Str, C Str List Str] = CallByName Inspect.43 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure Inspect.200 (Inspect.201, #Attr.12): - let Inspect.342 : Str = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Inspect.341 : Str = CallByName Inspect.59 Inspect.201 Inspect.342; - ret Inspect.341; - -procedure Inspect.202 (Inspect.203, #Attr.12): - let Inspect.336 : List Str = UnionAtIndex (Id 1) (Index 1) #Attr.12; - let Inspect.335 : Str = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Inspect.334 : Str = "("; - let Inspect.333 : Str = CallByName Inspect.59 Inspect.203 Inspect.334; - let Inspect.321 : Str = CallByName Inspect.59 Inspect.333 Inspect.335; - let Inspect.317 : Str = CallByName Inspect.204 Inspect.321 Inspect.336; - let Inspect.318 : Str = ")"; - let Inspect.316 : Str = CallByName Inspect.59 Inspect.317 Inspect.318; - ret Inspect.316; - -procedure Inspect.204 (Inspect.205, Inspect.199): - let Inspect.325 : {} = Struct {}; - let Inspect.324 : Str = CallByName List.18 Inspect.199 Inspect.205 Inspect.325; - ret Inspect.324; +procedure Inspect.204 (Inspect.205, #Attr.12): + let Inspect.346 : Str = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Inspect.345 : Str = CallByName Inspect.63 Inspect.205 Inspect.346; + ret Inspect.345; + +procedure Inspect.206 (Inspect.207, #Attr.12): + let Inspect.340 : List Str = UnionAtIndex (Id 1) (Index 1) #Attr.12; + let Inspect.339 : Str = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Inspect.338 : Str = "("; + let Inspect.337 : Str = CallByName Inspect.63 Inspect.207 Inspect.338; + let Inspect.325 : Str = CallByName Inspect.63 Inspect.337 Inspect.339; + let Inspect.321 : Str = CallByName Inspect.208 Inspect.325 Inspect.340; + let Inspect.322 : Str = ")"; + let Inspect.320 : Str = CallByName Inspect.63 Inspect.321 Inspect.322; + ret Inspect.320; -procedure Inspect.206 (Inspect.207, Inspect.208): - let Inspect.332 : Str = " "; - let Inspect.327 : Str = CallByName Inspect.59 Inspect.207 Inspect.332; - let Inspect.326 : Str = CallByName Inspect.209 Inspect.327 Inspect.208; - ret Inspect.326; +procedure Inspect.208 (Inspect.209, Inspect.203): + let Inspect.329 : {} = Struct {}; + let Inspect.328 : Str = CallByName List.18 Inspect.203 Inspect.209 Inspect.329; + ret Inspect.328; -procedure Inspect.209 (Inspect.210, Inspect.208): - let Inspect.330 : Str = CallByName Inspect.31 Inspect.208 Inspect.210; +procedure Inspect.210 (Inspect.211, Inspect.212): + let Inspect.336 : Str = " "; + let Inspect.331 : Str = CallByName Inspect.63 Inspect.211 Inspect.336; + let Inspect.330 : Str = CallByName Inspect.213 Inspect.331 Inspect.212; ret Inspect.330; -procedure Inspect.246 (Inspect.247, Inspect.245): - let Inspect.351 : Str = "\""; - let Inspect.350 : Str = CallByName Inspect.59 Inspect.247 Inspect.351; - let Inspect.348 : Str = CallByName Inspect.59 Inspect.350 Inspect.245; - let Inspect.349 : Str = "\""; - let Inspect.347 : Str = CallByName Inspect.59 Inspect.348 Inspect.349; - ret Inspect.347; +procedure Inspect.213 (Inspect.214, Inspect.212): + let Inspect.334 : Str = CallByName Inspect.31 Inspect.212 Inspect.214; + ret Inspect.334; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.355 : Str = "\""; + let Inspect.354 : Str = CallByName Inspect.63 Inspect.251 Inspect.355; + let Inspect.352 : Str = CallByName Inspect.63 Inspect.354 Inspect.249; + let Inspect.353 : Str = "\""; + let Inspect.351 : Str = CallByName Inspect.63 Inspect.352 Inspect.353; + ret Inspect.351; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.311 : U8 = GetTagId Inspect.299; - switch Inspect.311: +procedure Inspect.30 (Inspect.147): + ret Inspect.147; + +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.315 : U8 = GetTagId Inspect.303; + switch Inspect.315: case 0: - let Inspect.310 : Str = CallByName Inspect.200 Inspect.145 Inspect.299; - ret Inspect.310; + let Inspect.314 : Str = CallByName Inspect.204 Inspect.149 Inspect.303; + ret Inspect.314; default: - let Inspect.310 : Str = CallByName Inspect.202 Inspect.145 Inspect.299; - ret Inspect.310; + let Inspect.314 : Str = CallByName Inspect.206 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.331 : Str = CallByName Inspect.246 Inspect.145 Inspect.299; - ret Inspect.331; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.335 : Str = CallByName Inspect.250 Inspect.149 Inspect.303; + ret Inspect.335; + +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; + +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; + +procedure Inspect.43 (Inspect.202, Inspect.203): + inc Inspect.203; + let Inspect.341 : Int1 = CallByName List.1 Inspect.203; + if Inspect.341 then + dec Inspect.203; + let Inspect.343 : [C Str, C Str List Str] = TagId(0) Inspect.202; + let Inspect.342 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.343; + ret Inspect.342; + else + let Inspect.317 : [C Str, C Str List Str] = TagId(1) Inspect.202 Inspect.203; + let Inspect.316 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.317; + ret Inspect.316; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.47 (Inspect.249): + let Inspect.347 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.347; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : Str = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.3 Inspect.308 Inspect.312; ret Inspect.307; -procedure Inspect.39 (Inspect.198, Inspect.199): - inc Inspect.199; - let Inspect.337 : Int1 = CallByName List.1 Inspect.199; - if Inspect.337 then - dec Inspect.199; - let Inspect.339 : [C Str, C Str List Str] = TagId(0) Inspect.198; - let Inspect.338 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.339; - ret Inspect.338; - else - let Inspect.313 : [C Str, C Str List Str] = TagId(1) Inspect.198 Inspect.199; - let Inspect.312 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.313; - ret Inspect.312; - -procedure Inspect.43 (Inspect.245): - let Inspect.343 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.343; - -procedure Inspect.5 (Inspect.146): - let Inspect.308 : Str = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.3 Inspect.304 Inspect.308; - ret Inspect.303; - -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.320 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.320; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.324 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.324; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.1 (List.107): - let List.587 : U64 = CallByName List.6 List.107; - dec List.107; - let List.588 : U64 = 0i64; - let List.586 : Int1 = CallByName Bool.11 List.587 List.588; - ret List.586; +procedure List.1 (List.110): + let List.590 : U64 = CallByName List.6 List.110; + dec List.110; + let List.591 : U64 = 0i64; + let List.589 : Int1 = CallByName Bool.11 List.590 List.591; + ret List.589; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : Str = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : Str = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13, #Derived_gen.14): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : Str = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : Str = CallByName Inspect.206 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17, #Derived_gen.18): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : Str = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : Str = CallByName Inspect.210 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13 #Derived_gen.14; + jump List.580 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17 #Derived_gen.18; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.4 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt b/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt index 0b3722cc03b..a7109f8bad7 100644 --- a/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt +++ b/crates/compiler/test_mono/generated/inspect_derived_tag_two_payloads_string.txt @@ -10,169 +10,169 @@ procedure #Derived.4 (#Derived.5, #Derived.1): let #Derived.2 : Str = StructAtIndex 0 #Derived.1; let #Derived.3 : Str = StructAtIndex 1 #Derived.1; let #Derived_gen.7 : Str = "A"; - let #Derived_gen.9 : Str = CallByName Inspect.43 #Derived.2; - let #Derived_gen.10 : Str = CallByName Inspect.43 #Derived.3; + let #Derived_gen.9 : Str = CallByName Inspect.47 #Derived.2; + let #Derived_gen.10 : Str = CallByName Inspect.47 #Derived.3; let #Derived_gen.8 : List Str = Array [#Derived_gen.9, #Derived_gen.10]; - let #Derived_gen.6 : [C Str, C Str List Str] = CallByName Inspect.39 #Derived_gen.7 #Derived_gen.8; + let #Derived_gen.6 : [C Str, C Str List Str] = CallByName Inspect.43 #Derived_gen.7 #Derived_gen.8; jump #Derived_gen.5 #Derived_gen.6; procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure Inspect.200 (Inspect.201, #Attr.12): - let Inspect.342 : Str = UnionAtIndex (Id 0) (Index 0) #Attr.12; - let Inspect.341 : Str = CallByName Inspect.59 Inspect.201 Inspect.342; - ret Inspect.341; - -procedure Inspect.202 (Inspect.203, #Attr.12): - let Inspect.336 : List Str = UnionAtIndex (Id 1) (Index 1) #Attr.12; - let Inspect.335 : Str = UnionAtIndex (Id 1) (Index 0) #Attr.12; - let Inspect.334 : Str = "("; - let Inspect.333 : Str = CallByName Inspect.59 Inspect.203 Inspect.334; - let Inspect.321 : Str = CallByName Inspect.59 Inspect.333 Inspect.335; - let Inspect.317 : Str = CallByName Inspect.204 Inspect.321 Inspect.336; - let Inspect.318 : Str = ")"; - let Inspect.316 : Str = CallByName Inspect.59 Inspect.317 Inspect.318; - ret Inspect.316; - -procedure Inspect.204 (Inspect.205, Inspect.199): - let Inspect.325 : {} = Struct {}; - let Inspect.324 : Str = CallByName List.18 Inspect.199 Inspect.205 Inspect.325; - ret Inspect.324; +procedure Inspect.204 (Inspect.205, #Attr.12): + let Inspect.346 : Str = UnionAtIndex (Id 0) (Index 0) #Attr.12; + let Inspect.345 : Str = CallByName Inspect.63 Inspect.205 Inspect.346; + ret Inspect.345; + +procedure Inspect.206 (Inspect.207, #Attr.12): + let Inspect.340 : List Str = UnionAtIndex (Id 1) (Index 1) #Attr.12; + let Inspect.339 : Str = UnionAtIndex (Id 1) (Index 0) #Attr.12; + let Inspect.338 : Str = "("; + let Inspect.337 : Str = CallByName Inspect.63 Inspect.207 Inspect.338; + let Inspect.325 : Str = CallByName Inspect.63 Inspect.337 Inspect.339; + let Inspect.321 : Str = CallByName Inspect.208 Inspect.325 Inspect.340; + let Inspect.322 : Str = ")"; + let Inspect.320 : Str = CallByName Inspect.63 Inspect.321 Inspect.322; + ret Inspect.320; -procedure Inspect.206 (Inspect.207, Inspect.208): - let Inspect.332 : Str = " "; - let Inspect.327 : Str = CallByName Inspect.59 Inspect.207 Inspect.332; - let Inspect.326 : Str = CallByName Inspect.209 Inspect.327 Inspect.208; - ret Inspect.326; +procedure Inspect.208 (Inspect.209, Inspect.203): + let Inspect.329 : {} = Struct {}; + let Inspect.328 : Str = CallByName List.18 Inspect.203 Inspect.209 Inspect.329; + ret Inspect.328; -procedure Inspect.209 (Inspect.210, Inspect.208): - let Inspect.330 : Str = CallByName Inspect.31 Inspect.208 Inspect.210; +procedure Inspect.210 (Inspect.211, Inspect.212): + let Inspect.336 : Str = " "; + let Inspect.331 : Str = CallByName Inspect.63 Inspect.211 Inspect.336; + let Inspect.330 : Str = CallByName Inspect.213 Inspect.331 Inspect.212; ret Inspect.330; -procedure Inspect.246 (Inspect.247, Inspect.245): - let Inspect.351 : Str = "\""; - let Inspect.350 : Str = CallByName Inspect.59 Inspect.247 Inspect.351; - let Inspect.348 : Str = CallByName Inspect.59 Inspect.350 Inspect.245; - let Inspect.349 : Str = "\""; - let Inspect.347 : Str = CallByName Inspect.59 Inspect.348 Inspect.349; - ret Inspect.347; +procedure Inspect.213 (Inspect.214, Inspect.212): + let Inspect.334 : Str = CallByName Inspect.31 Inspect.212 Inspect.214; + ret Inspect.334; + +procedure Inspect.250 (Inspect.251, Inspect.249): + let Inspect.355 : Str = "\""; + let Inspect.354 : Str = CallByName Inspect.63 Inspect.251 Inspect.355; + let Inspect.352 : Str = CallByName Inspect.63 Inspect.354 Inspect.249; + let Inspect.353 : Str = "\""; + let Inspect.351 : Str = CallByName Inspect.63 Inspect.352 Inspect.353; + ret Inspect.351; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.311 : U8 = GetTagId Inspect.299; - switch Inspect.311: +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.315 : U8 = GetTagId Inspect.303; + switch Inspect.315: case 0: - let Inspect.310 : Str = CallByName Inspect.200 Inspect.145 Inspect.299; - ret Inspect.310; + let Inspect.314 : Str = CallByName Inspect.204 Inspect.149 Inspect.303; + ret Inspect.314; default: - let Inspect.310 : Str = CallByName Inspect.202 Inspect.145 Inspect.299; - ret Inspect.310; + let Inspect.314 : Str = CallByName Inspect.206 Inspect.149 Inspect.303; + ret Inspect.314; -procedure Inspect.31 (Inspect.299, Inspect.145): - let Inspect.331 : Str = CallByName Inspect.246 Inspect.145 Inspect.299; - ret Inspect.331; - -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; - -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; +procedure Inspect.31 (Inspect.303, Inspect.149): + let Inspect.335 : Str = CallByName Inspect.250 Inspect.149 Inspect.303; + ret Inspect.335; + +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; + +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; + +procedure Inspect.43 (Inspect.202, Inspect.203): + inc Inspect.203; + let Inspect.341 : Int1 = CallByName List.1 Inspect.203; + if Inspect.341 then + dec Inspect.203; + let Inspect.343 : [C Str, C Str List Str] = TagId(0) Inspect.202; + let Inspect.342 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.343; + ret Inspect.342; + else + let Inspect.317 : [C Str, C Str List Str] = TagId(1) Inspect.202 Inspect.203; + let Inspect.316 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.317; + ret Inspect.316; + +procedure Inspect.47 (Inspect.249): + let Inspect.356 : Str = CallByName Inspect.30 Inspect.249; + ret Inspect.356; + +procedure Inspect.5 (Inspect.150): + let Inspect.312 : {Str, Str} = CallByName #Derived.0 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName #Derived.4 Inspect.308 Inspect.312; ret Inspect.307; -procedure Inspect.39 (Inspect.198, Inspect.199): - inc Inspect.199; - let Inspect.337 : Int1 = CallByName List.1 Inspect.199; - if Inspect.337 then - dec Inspect.199; - let Inspect.339 : [C Str, C Str List Str] = TagId(0) Inspect.198; - let Inspect.338 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.339; - ret Inspect.338; - else - let Inspect.313 : [C Str, C Str List Str] = TagId(1) Inspect.198 Inspect.199; - let Inspect.312 : [C Str, C Str List Str] = CallByName Inspect.30 Inspect.313; - ret Inspect.312; - -procedure Inspect.43 (Inspect.245): - let Inspect.352 : Str = CallByName Inspect.30 Inspect.245; - ret Inspect.352; - -procedure Inspect.5 (Inspect.146): - let Inspect.308 : {Str, Str} = CallByName #Derived.0 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName #Derived.4 Inspect.304 Inspect.308; - ret Inspect.303; - -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.320 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.320; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.324 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.324; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; -procedure List.1 (List.107): - let List.587 : U64 = CallByName List.6 List.107; - dec List.107; - let List.588 : U64 = 0i64; - let List.586 : Int1 = CallByName Bool.11 List.587 List.588; - ret List.586; +procedure List.1 (List.110): + let List.590 : U64 = CallByName List.6 List.110; + dec List.110; + let List.591 : U64 = 0i64; + let List.589 : Int1 = CallByName Bool.11 List.590 List.591; + ret List.589; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : Str = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : Str = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; - -procedure List.92 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : Str = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : Str = CallByName Inspect.206 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.587 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; + +procedure List.95 (#Derived_gen.13, #Derived_gen.14, #Derived_gen.15, #Derived_gen.16, #Derived_gen.17): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : Str = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : Str = CallByName Inspect.210 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; + jump List.580 #Derived_gen.13 #Derived_gen.14 #Derived_gen.15 #Derived_gen.16 #Derived_gen.17; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.5 : Str = "foo"; diff --git a/crates/compiler/test_mono/generated/ir_int_add.txt b/crates/compiler/test_mono/generated/ir_int_add.txt index 492c35e956d..367bd40fd2c 100644 --- a/crates/compiler/test_mono/generated/ir_int_add.txt +++ b/crates/compiler/test_mono/generated/ir_int_add.txt @@ -1,10 +1,10 @@ procedure List.6 (#Attr.2): - let List.574 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.574; + let List.577 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.577; procedure Num.19 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.283; procedure Test.0 (): let Test.1 : List I64 = Array [1i64, 2i64]; diff --git a/crates/compiler/test_mono/generated/ir_plus.txt b/crates/compiler/test_mono/generated/ir_plus.txt index 4e98a70eae3..29a356d1023 100644 --- a/crates/compiler/test_mono/generated/ir_plus.txt +++ b/crates/compiler/test_mono/generated/ir_plus.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.2 : I64 = 1i64; diff --git a/crates/compiler/test_mono/generated/ir_round.txt b/crates/compiler/test_mono/generated/ir_round.txt index bd95abe9e51..61cef3d782e 100644 --- a/crates/compiler/test_mono/generated/ir_round.txt +++ b/crates/compiler/test_mono/generated/ir_round.txt @@ -1,6 +1,6 @@ procedure Num.45 (#Attr.2): - let Num.279 : I64 = lowlevel NumRound #Attr.2; - ret Num.279; + let Num.281 : I64 = lowlevel NumRound #Attr.2; + ret Num.281; procedure Test.0 (): let Test.2 : Decimal = 3.6dec; diff --git a/crates/compiler/test_mono/generated/ir_two_defs.txt b/crates/compiler/test_mono/generated/ir_two_defs.txt index 915eb3acd42..58534f57dd5 100644 --- a/crates/compiler/test_mono/generated/ir_two_defs.txt +++ b/crates/compiler/test_mono/generated/ir_two_defs.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.1 : I64 = 3i64; diff --git a/crates/compiler/test_mono/generated/ir_when_idiv.txt b/crates/compiler/test_mono/generated/ir_when_idiv.txt index 0c17b3f39a3..1e1cbccb19c 100644 --- a/crates/compiler/test_mono/generated/ir_when_idiv.txt +++ b/crates/compiler/test_mono/generated/ir_when_idiv.txt @@ -1,22 +1,22 @@ procedure Num.157 (#Attr.2, #Attr.3): - let Num.281 : I64 = lowlevel NumDivTruncUnchecked #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : I64 = lowlevel NumDivTruncUnchecked #Attr.2 #Attr.3; + ret Num.283; procedure Num.30 (#Attr.2): - let Num.286 : I64 = 0i64; - let Num.285 : Int1 = lowlevel Eq #Attr.2 Num.286; - ret Num.285; + let Num.288 : I64 = 0i64; + let Num.287 : Int1 = lowlevel Eq #Attr.2 Num.288; + ret Num.287; -procedure Num.40 (Num.247, Num.248): - let Num.282 : Int1 = CallByName Num.30 Num.248; - if Num.282 then - let Num.284 : {} = Struct {}; - let Num.283 : [C {}, C I64] = TagId(0) Num.284; - ret Num.283; +procedure Num.40 (Num.249, Num.250): + let Num.284 : Int1 = CallByName Num.30 Num.250; + if Num.284 then + let Num.286 : {} = Struct {}; + let Num.285 : [C {}, C I64] = TagId(0) Num.286; + ret Num.285; else - let Num.280 : I64 = CallByName Num.157 Num.247 Num.248; - let Num.279 : [C {}, C I64] = TagId(1) Num.280; - ret Num.279; + let Num.282 : I64 = CallByName Num.157 Num.249 Num.250; + let Num.281 : [C {}, C I64] = TagId(1) Num.282; + ret Num.281; procedure Test.0 (): let Test.8 : I64 = 1000i64; diff --git a/crates/compiler/test_mono/generated/ir_when_just.txt b/crates/compiler/test_mono/generated/ir_when_just.txt index 316d2e16f62..b237e4b6abf 100644 --- a/crates/compiler/test_mono/generated/ir_when_just.txt +++ b/crates/compiler/test_mono/generated/ir_when_just.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.10 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt index 73f216c8e0e..080210cdaf0 100644 --- a/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt +++ b/crates/compiler/test_mono/generated/issue_2583_specialize_errors_behind_unified_branches.txt @@ -6,69 +6,69 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure List.2 (List.108, List.109): - let List.588 : U64 = CallByName List.6 List.108; - let List.584 : Int1 = CallByName Num.22 List.109 List.588; - if List.584 then - let List.586 : I64 = CallByName List.66 List.108 List.109; - dec List.108; - let List.585 : [C {}, C I64] = TagId(1) List.586; - ret List.585; +procedure List.2 (List.111, List.112): + let List.591 : U64 = CallByName List.6 List.111; + let List.587 : Int1 = CallByName Num.22 List.112 List.591; + if List.587 then + let List.589 : I64 = CallByName List.66 List.111 List.112; + dec List.111; + let List.588 : [C {}, C I64] = TagId(1) List.589; + ret List.588; else - dec List.108; - let List.583 : {} = Struct {}; - let List.582 : [C {}, C I64] = TagId(0) List.583; - ret List.582; + dec List.111; + let List.586 : {} = Struct {}; + let List.585 : [C {}, C I64] = TagId(0) List.586; + ret List.585; procedure List.6 (#Attr.2): - let List.589 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.589; + let List.592 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.592; procedure List.66 (#Attr.2, #Attr.3): - let List.587 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.587; + let List.590 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.590; -procedure List.9 (List.335): - let List.581 : U64 = 0i64; - let List.574 : [C {}, C I64] = CallByName List.2 List.335 List.581; - let List.578 : U8 = 1i64; - let List.579 : U8 = GetTagId List.574; - let List.580 : Int1 = lowlevel Eq List.578 List.579; - if List.580 then - let List.336 : I64 = UnionAtIndex (Id 1) (Index 0) List.574; - let List.575 : [C Int1, C I64] = TagId(1) List.336; - ret List.575; +procedure List.9 (List.338): + let List.584 : U64 = 0i64; + let List.577 : [C {}, C I64] = CallByName List.2 List.338 List.584; + let List.581 : U8 = 1i64; + let List.582 : U8 = GetTagId List.577; + let List.583 : Int1 = lowlevel Eq List.581 List.582; + if List.583 then + let List.339 : I64 = UnionAtIndex (Id 1) (Index 0) List.577; + let List.578 : [C Int1, C I64] = TagId(1) List.339; + ret List.578; else - let List.577 : Int1 = true; - let List.576 : [C Int1, C I64] = TagId(0) List.577; - ret List.576; + let List.580 : Int1 = true; + let List.579 : [C Int1, C I64] = TagId(0) List.580; + ret List.579; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; -procedure Str.27 (Str.78): - let Str.232 : [C Int1, C I64] = CallByName Str.60 Str.78; - ret Str.232; +procedure Str.27 (Str.82): + let Str.236 : [C Int1, C I64] = CallByName Str.64 Str.82; + ret Str.236; procedure Str.42 (#Attr.2): - let Str.240 : {I64, U8} = lowlevel StrToNum #Attr.2; - ret Str.240; + let Str.244 : {I64, U8} = lowlevel StrToNum #Attr.2; + ret Str.244; -procedure Str.60 (Str.185): - let Str.186 : {I64, U8} = CallByName Str.42 Str.185; - dec Str.185; - let Str.238 : U8 = StructAtIndex 1 Str.186; - let Str.239 : U8 = 0i64; - let Str.235 : Int1 = CallByName Bool.11 Str.238 Str.239; - if Str.235 then - let Str.237 : I64 = StructAtIndex 0 Str.186; - let Str.236 : [C Int1, C I64] = TagId(1) Str.237; - ret Str.236; +procedure Str.64 (Str.189): + let Str.190 : {I64, U8} = CallByName Str.42 Str.189; + dec Str.189; + let Str.242 : U8 = StructAtIndex 1 Str.190; + let Str.243 : U8 = 0i64; + let Str.239 : Int1 = CallByName Bool.11 Str.242 Str.243; + if Str.239 then + let Str.241 : I64 = StructAtIndex 0 Str.190; + let Str.240 : [C Int1, C I64] = TagId(1) Str.241; + ret Str.240; else - let Str.234 : Int1 = false; - let Str.233 : [C Int1, C I64] = TagId(0) Str.234; - ret Str.233; + let Str.238 : Int1 = false; + let Str.237 : [C Int1, C I64] = TagId(0) Str.238; + ret Str.237; procedure Test.0 (): let Test.3 : Int1 = CallByName Bool.2; diff --git a/crates/compiler/test_mono/generated/issue_4770.txt b/crates/compiler/test_mono/generated/issue_4770.txt index 756daa988ca..90c8599c708 100644 --- a/crates/compiler/test_mono/generated/issue_4770.txt +++ b/crates/compiler/test_mono/generated/issue_4770.txt @@ -6,92 +6,92 @@ procedure Bool.2 (): let Bool.24 : Int1 = true; ret Bool.24; -procedure List.104 (List.488, List.489, List.490): - let List.588 : U64 = 0i64; - let List.589 : U64 = CallByName List.6 List.488; - let List.587 : [C {}, C {}] = CallByName List.80 List.488 List.489 List.490 List.588 List.589; - ret List.587; +procedure List.107 (List.491, List.492, List.493): + let List.591 : U64 = 0i64; + let List.592 : U64 = CallByName List.6 List.491; + let List.590 : [C {}, C {}] = CallByName List.80 List.491 List.492 List.493 List.591 List.592; + ret List.590; procedure List.23 (#Attr.2, #Attr.3, #Attr.4): - let List.609 : List {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListMap2 { xs: `#Attr.#arg1`, ys: `#Attr.#arg2` } #Attr.2 #Attr.3 Test.15 #Attr.4; + let List.612 : List {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListMap2 { xs: `#Attr.#arg1`, ys: `#Attr.#arg2` } #Attr.2 #Attr.3 Test.15 #Attr.4; decref #Attr.3; decref #Attr.2; - ret List.609; + ret List.612; -procedure List.236 (List.576, List.237, List.235): - let List.606 : Int1 = CallByName Test.1 List.237; - if List.606 then +procedure List.239 (List.579, List.240, List.238): + let List.609 : Int1 = CallByName Test.1 List.240; + if List.609 then + let List.611 : {} = Struct {}; + let List.610 : [C {}, C {}] = TagId(1) List.611; + ret List.610; + else let List.608 : {} = Struct {}; - let List.607 : [C {}, C {}] = TagId(1) List.608; + let List.607 : [C {}, C {}] = TagId(0) List.608; ret List.607; - else - let List.605 : {} = Struct {}; - let List.604 : [C {}, C {}] = TagId(0) List.605; - ret List.604; -procedure List.56 (List.234, List.235): - let List.585 : {} = Struct {}; - let List.577 : [C {}, C {}] = CallByName List.104 List.234 List.585 List.235; - let List.582 : U8 = 1i64; - let List.583 : U8 = GetTagId List.577; - let List.584 : Int1 = lowlevel Eq List.582 List.583; - if List.584 then - let List.578 : Int1 = CallByName Bool.2; - ret List.578; +procedure List.56 (List.237, List.238): + let List.588 : {} = Struct {}; + let List.580 : [C {}, C {}] = CallByName List.107 List.237 List.588 List.238; + let List.585 : U8 = 1i64; + let List.586 : U8 = GetTagId List.580; + let List.587 : Int1 = lowlevel Eq List.585 List.586; + if List.587 then + let List.581 : Int1 = CallByName Bool.2; + ret List.581; else - let List.579 : Int1 = CallByName Bool.1; - ret List.579; + let List.582 : Int1 = CallByName Bool.1; + ret List.582; procedure List.6 (#Attr.2): - let List.575 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.575; + let List.578 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.578; procedure List.6 (#Attr.2): - let List.603 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.603; + let List.606 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.606; procedure List.66 (#Attr.2, #Attr.3): - let List.602 : {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.602; + let List.605 : {[C I64, C List *self], [C I64, C List *self]} = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.605; procedure List.80 (#Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4, #Derived_gen.5): - joinpoint List.590 List.491 List.492 List.493 List.494 List.495: - let List.592 : Int1 = CallByName Num.22 List.494 List.495; - if List.592 then - let List.601 : {[C I64, C List *self], [C I64, C List *self]} = CallByName List.66 List.491 List.494; - inc List.601; - let List.593 : [C {}, C {}] = CallByName List.236 List.492 List.601 List.493; - let List.598 : U8 = 1i64; - let List.599 : U8 = GetTagId List.593; - let List.600 : Int1 = lowlevel Eq List.598 List.599; - if List.600 then - let List.496 : {} = UnionAtIndex (Id 1) (Index 0) List.593; - let List.596 : U64 = 1i64; - let List.595 : U64 = CallByName Num.51 List.494 List.596; - jump List.590 List.491 List.496 List.493 List.595 List.495; + joinpoint List.593 List.494 List.495 List.496 List.497 List.498: + let List.595 : Int1 = CallByName Num.22 List.497 List.498; + if List.595 then + let List.604 : {[C I64, C List *self], [C I64, C List *self]} = CallByName List.66 List.494 List.497; + inc List.604; + let List.596 : [C {}, C {}] = CallByName List.239 List.495 List.604 List.496; + let List.601 : U8 = 1i64; + let List.602 : U8 = GetTagId List.596; + let List.603 : Int1 = lowlevel Eq List.601 List.602; + if List.603 then + let List.499 : {} = UnionAtIndex (Id 1) (Index 0) List.596; + let List.599 : U64 = 1i64; + let List.598 : U64 = CallByName Num.51 List.497 List.599; + jump List.593 List.494 List.499 List.496 List.598 List.498; else - dec List.491; - let List.497 : {} = UnionAtIndex (Id 0) (Index 0) List.593; - let List.597 : [C {}, C {}] = TagId(0) List.497; - ret List.597; + dec List.494; + let List.500 : {} = UnionAtIndex (Id 0) (Index 0) List.596; + let List.600 : [C {}, C {}] = TagId(0) List.500; + ret List.600; else - dec List.491; - let List.591 : [C {}, C {}] = TagId(1) List.492; - ret List.591; + dec List.494; + let List.594 : [C {}, C {}] = TagId(1) List.495; + ret List.594; in - jump List.590 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5; + jump List.593 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4 #Derived_gen.5; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Test.1 (#Derived_gen.0): joinpoint Test.26 Test.6: diff --git a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt index 5f34b99efd7..2a3716fb0f7 100644 --- a/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt +++ b/crates/compiler/test_mono/generated/issue_4772_weakened_monomorphic_destructure.txt @@ -6,44 +6,44 @@ procedure Bool.11 (#Attr.2, #Attr.3): let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.24; -procedure Decode.24 (Decode.101): - ret Decode.101; +procedure Decode.24 (Decode.105): + ret Decode.105; -procedure Decode.25 (Decode.102, Decode.121, Decode.104): - let Decode.124 : {List U8, [C {}, C Str]} = CallByName Test.76 Decode.102 Decode.104; - ret Decode.124; +procedure Decode.25 (Decode.106, Decode.125, Decode.108): + let Decode.128 : {List U8, [C {}, C Str]} = CallByName Test.76 Decode.106 Decode.108; + ret Decode.128; -procedure Decode.26 (Decode.105, Decode.106): - let Decode.123 : {} = CallByName Test.15; - let Decode.122 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.105 Decode.123 Decode.106; - ret Decode.122; +procedure Decode.26 (Decode.109, Decode.110): + let Decode.127 : {} = CallByName Test.15; + let Decode.126 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.109 Decode.127 Decode.110; + ret Decode.126; procedure Str.12 (#Attr.2): - let Str.241 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.241; + let Str.245 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.245; -procedure Str.27 (Str.78): - let Str.232 : [C {}, C I64] = CallByName Str.60 Str.78; - ret Str.232; +procedure Str.27 (Str.82): + let Str.236 : [C {}, C I64] = CallByName Str.64 Str.82; + ret Str.236; procedure Str.42 (#Attr.2): - let Str.240 : {I64, U8} = lowlevel StrToNum #Attr.2; - ret Str.240; + let Str.244 : {I64, U8} = lowlevel StrToNum #Attr.2; + ret Str.244; -procedure Str.60 (Str.185): - let Str.186 : {I64, U8} = CallByName Str.42 Str.185; - dec Str.185; - let Str.238 : U8 = StructAtIndex 1 Str.186; - let Str.239 : U8 = 0i64; - let Str.235 : Int1 = CallByName Bool.11 Str.238 Str.239; - if Str.235 then - let Str.237 : I64 = StructAtIndex 0 Str.186; - let Str.236 : [C {}, C I64] = TagId(1) Str.237; - ret Str.236; +procedure Str.64 (Str.189): + let Str.190 : {I64, U8} = CallByName Str.42 Str.189; + dec Str.189; + let Str.242 : U8 = StructAtIndex 1 Str.190; + let Str.243 : U8 = 0i64; + let Str.239 : Int1 = CallByName Bool.11 Str.242 Str.243; + if Str.239 then + let Str.241 : I64 = StructAtIndex 0 Str.190; + let Str.240 : [C {}, C I64] = TagId(1) Str.241; + ret Str.240; else - let Str.234 : {} = Struct {}; - let Str.233 : [C {}, C I64] = TagId(0) Str.234; - ret Str.233; + let Str.238 : {} = Struct {}; + let Str.237 : [C {}, C I64] = TagId(0) Str.238; + ret Str.237; procedure Test.103 (): let Test.101 : [C Str, C {List U8, I64}] = CallByName Test.19; diff --git a/crates/compiler/test_mono/generated/issue_6196.txt b/crates/compiler/test_mono/generated/issue_6196.txt index 8ca6187a27d..bcdb996807a 100644 --- a/crates/compiler/test_mono/generated/issue_6196.txt +++ b/crates/compiler/test_mono/generated/issue_6196.txt @@ -1,6 +1,6 @@ procedure Num.20 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.12 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt index 45025071cb4..de4d802f411 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niche_u8_vs_u64.txt @@ -1,10 +1,10 @@ procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Num.96 (#Attr.2): - let Num.280 : Str = lowlevel NumToStr #Attr.2; - ret Num.280; + let Num.282 : Str = lowlevel NumToStr #Attr.2; + ret Num.282; procedure Test.1 (Test.4): let Test.13 : [C U8, C U64] = TagId(1) Test.4; diff --git a/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt b/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt index c95a0adc440..5231fbd3ed1 100644 --- a/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt +++ b/crates/compiler/test_mono/generated/lambda_capture_niches_with_other_lambda_capture.txt @@ -1,6 +1,6 @@ procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.1 (Test.5): let Test.16 : [C {}, C U64, C Str] = TagId(0) Test.5; diff --git a/crates/compiler/test_mono/generated/lambda_set_with_imported_toplevels_issue_4733.txt b/crates/compiler/test_mono/generated/lambda_set_with_imported_toplevels_issue_4733.txt index 55ef9d7afa6..de65cbc2da9 100644 --- a/crates/compiler/test_mono/generated/lambda_set_with_imported_toplevels_issue_4733.txt +++ b/crates/compiler/test_mono/generated/lambda_set_with_imported_toplevels_issue_4733.txt @@ -7,12 +7,12 @@ procedure Bool.2 (): ret Bool.24; procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Num.21 (#Attr.2, #Attr.3): - let Num.280 : U64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.282; procedure Test.0 (Test.8): let Test.20 : Int1 = CallByName Bool.2; diff --git a/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt b/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt index 5e35aac0992..4d614df1770 100644 --- a/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt +++ b/crates/compiler/test_mono/generated/layout_cache_structure_with_multiple_recursive_structures.txt @@ -1,40 +1,40 @@ -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : [, C {[C *self, ], *self}] = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : [, C {[C *self, ], *self}] = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; procedure List.6 (#Attr.2): - let List.585 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.585; + let List.588 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.588; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : [C *self, ] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : [C *self, ] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; -procedure List.92 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : [C *self, ] = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : [, C {[C *self, ], *self}] = CallByName Test.7 List.164 List.583; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; +procedure List.95 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : [C *self, ] = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : [, C {[C *self, ], *self}] = CallByName Test.7 List.167 List.586; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.580 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Num.51 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.281; procedure Test.7 (Test.11, Test.12): let Test.17 : {[C *self, ], [, C {[C *self, ], *self}]} = Struct {Test.12, Test.11}; diff --git a/crates/compiler/test_mono/generated/linked_list_filter.txt b/crates/compiler/test_mono/generated/linked_list_filter.txt index 04e3c3ff0d5..08893f2bcb2 100644 --- a/crates/compiler/test_mono/generated/linked_list_filter.txt +++ b/crates/compiler/test_mono/generated/linked_list_filter.txt @@ -1,11 +1,11 @@ -procedure Num.31 (Num.217): - let Num.280 : I64 = 2i64; - let Num.279 : Int1 = CallByName Num.86 Num.217 Num.280; - ret Num.279; +procedure Num.31 (Num.219): + let Num.282 : I64 = 2i64; + let Num.281 : Int1 = CallByName Num.86 Num.219 Num.282; + ret Num.281; procedure Num.86 (#Attr.2, #Attr.3): - let Num.281 : Int1 = lowlevel NumIsMultipleOf #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : Int1 = lowlevel NumIsMultipleOf #Attr.2 #Attr.3; + ret Num.283; procedure Test.2 (#Derived_gen.0, #Derived_gen.1): let #Derived_gen.3 : [, C I64 *self] = NullPointer; diff --git a/crates/compiler/test_mono/generated/linked_list_map.txt b/crates/compiler/test_mono/generated/linked_list_map.txt index 0638cfb231e..3a21360c880 100644 --- a/crates/compiler/test_mono/generated/linked_list_map.txt +++ b/crates/compiler/test_mono/generated/linked_list_map.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.10 (Test.11): let Test.28 : I64 = 1i64; diff --git a/crates/compiler/test_mono/generated/list_append.txt b/crates/compiler/test_mono/generated/list_append.txt index f576eec68c9..5f3dcfd02ed 100644 --- a/crates/compiler/test_mono/generated/list_append.txt +++ b/crates/compiler/test_mono/generated/list_append.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.124, List.125): - let List.577 : U64 = 1i64; - let List.575 : List I64 = CallByName List.70 List.124 List.577; - let List.574 : List I64 = CallByName List.71 List.575 List.125; - ret List.574; +procedure List.4 (List.127, List.128): + let List.580 : U64 = 1i64; + let List.578 : List I64 = CallByName List.70 List.127 List.580; + let List.577 : List I64 = CallByName List.71 List.578 List.128; + ret List.577; procedure List.70 (#Attr.2, #Attr.3): - let List.578 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.578; + let List.581 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.581; procedure List.71 (#Attr.2, #Attr.3): - let List.576 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.576; + let List.579 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.579; procedure Test.0 (): let Test.2 : List I64 = Array [1i64]; diff --git a/crates/compiler/test_mono/generated/list_append_closure.txt b/crates/compiler/test_mono/generated/list_append_closure.txt index 7c9b34ed89f..3800281470a 100644 --- a/crates/compiler/test_mono/generated/list_append_closure.txt +++ b/crates/compiler/test_mono/generated/list_append_closure.txt @@ -1,16 +1,16 @@ -procedure List.4 (List.124, List.125): - let List.577 : U64 = 1i64; - let List.575 : List I64 = CallByName List.70 List.124 List.577; - let List.574 : List I64 = CallByName List.71 List.575 List.125; - ret List.574; +procedure List.4 (List.127, List.128): + let List.580 : U64 = 1i64; + let List.578 : List I64 = CallByName List.70 List.127 List.580; + let List.577 : List I64 = CallByName List.71 List.578 List.128; + ret List.577; procedure List.70 (#Attr.2, #Attr.3): - let List.578 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.578; + let List.581 : List I64 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.581; procedure List.71 (#Attr.2, #Attr.3): - let List.576 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.576; + let List.579 : List I64 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.579; procedure Test.1 (Test.2): let Test.6 : I64 = 42i64; diff --git a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt index 7c0fdb636dc..9e2db14ad96 100644 --- a/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt +++ b/crates/compiler/test_mono/generated/list_cannot_update_inplace.txt @@ -1,33 +1,33 @@ -procedure List.3 (List.116, List.117, List.118): - let List.577 : {List I64, I64} = CallByName List.64 List.116 List.117 List.118; - let List.576 : List I64 = StructAtIndex 0 List.577; - ret List.576; +procedure List.3 (List.119, List.120, List.121): + let List.580 : {List I64, I64} = CallByName List.64 List.119 List.120 List.121; + let List.579 : List I64 = StructAtIndex 0 List.580; + ret List.579; procedure List.6 (#Attr.2): - let List.575 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.575; - -procedure List.64 (List.113, List.114, List.115): - let List.582 : U64 = CallByName List.6 List.113; - let List.579 : Int1 = CallByName Num.22 List.114 List.582; - if List.579 then - let List.580 : {List I64, I64} = CallByName List.67 List.113 List.114 List.115; - ret List.580; + let List.578 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.578; + +procedure List.64 (List.116, List.117, List.118): + let List.585 : U64 = CallByName List.6 List.116; + let List.582 : Int1 = CallByName Num.22 List.117 List.585; + if List.582 then + let List.583 : {List I64, I64} = CallByName List.67 List.116 List.117 List.118; + ret List.583; else - let List.578 : {List I64, I64} = Struct {List.113, List.115}; - ret List.578; + let List.581 : {List I64, I64} = Struct {List.116, List.118}; + ret List.581; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.581 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.581; + let List.584 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.584; procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Num.22 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.282; procedure Test.1 (): let Test.8 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_get.txt b/crates/compiler/test_mono/generated/list_get.txt index fbab3b2d476..9535a1b192a 100644 --- a/crates/compiler/test_mono/generated/list_get.txt +++ b/crates/compiler/test_mono/generated/list_get.txt @@ -1,28 +1,28 @@ -procedure List.2 (List.108, List.109): - let List.580 : U64 = CallByName List.6 List.108; - let List.576 : Int1 = CallByName Num.22 List.109 List.580; - if List.576 then - let List.578 : I64 = CallByName List.66 List.108 List.109; - dec List.108; - let List.577 : [C {}, C I64] = TagId(1) List.578; - ret List.577; +procedure List.2 (List.111, List.112): + let List.583 : U64 = CallByName List.6 List.111; + let List.579 : Int1 = CallByName Num.22 List.112 List.583; + if List.579 then + let List.581 : I64 = CallByName List.66 List.111 List.112; + dec List.111; + let List.580 : [C {}, C I64] = TagId(1) List.581; + ret List.580; else - dec List.108; - let List.575 : {} = Struct {}; - let List.574 : [C {}, C I64] = TagId(0) List.575; - ret List.574; + dec List.111; + let List.578 : {} = Struct {}; + let List.577 : [C {}, C I64] = TagId(0) List.578; + ret List.577; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; procedure List.66 (#Attr.2, #Attr.3): - let List.579 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.579; + let List.582 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2): let Test.6 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_len.txt b/crates/compiler/test_mono/generated/list_len.txt index 2ef06e09855..e4f8d28e5db 100644 --- a/crates/compiler/test_mono/generated/list_len.txt +++ b/crates/compiler/test_mono/generated/list_len.txt @@ -1,14 +1,14 @@ procedure List.6 (#Attr.2): - let List.574 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.574; + let List.577 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.577; procedure List.6 (#Attr.2): - let List.575 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.575; + let List.578 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.578; procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.1 : List I64 = Array [1i64, 2i64, 3i64]; diff --git a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt index 4bf738ccd25..984c1d7938d 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_borrows.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_borrows.txt @@ -1,42 +1,42 @@ -procedure List.2 (List.108, List.109): - let List.580 : U64 = CallByName List.6 List.108; - let List.576 : Int1 = CallByName Num.22 List.109 List.580; - if List.576 then - let List.578 : Str = CallByName List.66 List.108 List.109; - inc List.578; - dec List.108; - let List.577 : [C {}, C Str] = TagId(1) List.578; - ret List.577; +procedure List.2 (List.111, List.112): + let List.583 : U64 = CallByName List.6 List.111; + let List.579 : Int1 = CallByName Num.22 List.112 List.583; + if List.579 then + let List.581 : Str = CallByName List.66 List.111 List.112; + inc List.581; + dec List.111; + let List.580 : [C {}, C Str] = TagId(1) List.581; + ret List.580; else - dec List.108; - let List.575 : {} = Struct {}; - let List.574 : [C {}, C Str] = TagId(0) List.575; - ret List.574; + dec List.111; + let List.578 : {} = Struct {}; + let List.577 : [C {}, C Str] = TagId(0) List.578; + ret List.577; procedure List.5 (#Attr.2, #Attr.3): - let List.582 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + let List.585 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; decref #Attr.2; - ret List.582; + ret List.585; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; procedure List.66 (#Attr.2, #Attr.3): - let List.579 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.579; + let List.582 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Str.16 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrRepeat #Attr.2 #Attr.3; + ret Str.236; procedure Str.3 (#Attr.2, #Attr.3): - let Str.233 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.233; + let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.237; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_map_closure_owns.txt b/crates/compiler/test_mono/generated/list_map_closure_owns.txt index fb492f961aa..40b06dc612c 100644 --- a/crates/compiler/test_mono/generated/list_map_closure_owns.txt +++ b/crates/compiler/test_mono/generated/list_map_closure_owns.txt @@ -1,38 +1,38 @@ -procedure List.2 (List.108, List.109): - let List.580 : U64 = CallByName List.6 List.108; - let List.576 : Int1 = CallByName Num.22 List.109 List.580; - if List.576 then - let List.578 : Str = CallByName List.66 List.108 List.109; - inc List.578; - dec List.108; - let List.577 : [C {}, C Str] = TagId(1) List.578; - ret List.577; +procedure List.2 (List.111, List.112): + let List.583 : U64 = CallByName List.6 List.111; + let List.579 : Int1 = CallByName Num.22 List.112 List.583; + if List.579 then + let List.581 : Str = CallByName List.66 List.111 List.112; + inc List.581; + dec List.111; + let List.580 : [C {}, C Str] = TagId(1) List.581; + ret List.580; else - dec List.108; - let List.575 : {} = Struct {}; - let List.574 : [C {}, C Str] = TagId(0) List.575; - ret List.574; + dec List.111; + let List.578 : {} = Struct {}; + let List.577 : [C {}, C Str] = TagId(0) List.578; + ret List.577; procedure List.5 (#Attr.2, #Attr.3): - let List.582 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; + let List.585 : List Str = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.3 #Attr.3; decref #Attr.2; - ret List.582; + ret List.585; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; procedure List.66 (#Attr.2, #Attr.3): - let List.579 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.579; + let List.582 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.233 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.233; + let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.237; procedure Test.1 (): let Test.21 : Str = "lllllllllllllllllllllooooooooooong"; diff --git a/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt b/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt index 9bed8ce12bd..6ee35680dc2 100644 --- a/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt +++ b/crates/compiler/test_mono/generated/list_map_take_capturing_or_noncapturing.txt @@ -1,28 +1,28 @@ procedure List.5 (#Attr.2, #Attr.3): - let List.575 : U8 = GetTagId #Attr.3; - joinpoint List.576 List.574: - ret List.574; + let List.578 : U8 = GetTagId #Attr.3; + joinpoint List.579 List.577: + ret List.577; in - switch List.575: + switch List.578: case 0: - let List.577 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.4 #Attr.3; + let List.580 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.4 #Attr.3; decref #Attr.2; - jump List.576 List.577; + jump List.579 List.580; case 1: - let List.578 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.6 #Attr.3; + let List.581 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.6 #Attr.3; decref #Attr.2; - jump List.576 List.578; + jump List.579 List.581; default: - let List.579 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.8 #Attr.3; + let List.582 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.8 #Attr.3; decref #Attr.2; - jump List.576 List.579; + jump List.579 List.582; procedure Num.19 (#Attr.2, #Attr.3): - let Num.281 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.283; procedure Test.4 (Test.5, #Attr.12): let Test.16 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12; diff --git a/crates/compiler/test_mono/generated/list_pass_to_function.txt b/crates/compiler/test_mono/generated/list_pass_to_function.txt index f8446ec6eaf..0345e77a79d 100644 --- a/crates/compiler/test_mono/generated/list_pass_to_function.txt +++ b/crates/compiler/test_mono/generated/list_pass_to_function.txt @@ -1,29 +1,29 @@ -procedure List.3 (List.116, List.117, List.118): - let List.575 : {List I64, I64} = CallByName List.64 List.116 List.117 List.118; - let List.574 : List I64 = StructAtIndex 0 List.575; - ret List.574; +procedure List.3 (List.119, List.120, List.121): + let List.578 : {List I64, I64} = CallByName List.64 List.119 List.120 List.121; + let List.577 : List I64 = StructAtIndex 0 List.578; + ret List.577; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; -procedure List.64 (List.113, List.114, List.115): - let List.580 : U64 = CallByName List.6 List.113; - let List.577 : Int1 = CallByName Num.22 List.114 List.580; - if List.577 then - let List.578 : {List I64, I64} = CallByName List.67 List.113 List.114 List.115; - ret List.578; +procedure List.64 (List.116, List.117, List.118): + let List.583 : U64 = CallByName List.6 List.116; + let List.580 : Int1 = CallByName Num.22 List.117 List.583; + if List.580 then + let List.581 : {List I64, I64} = CallByName List.67 List.116 List.117 List.118; + ret List.581; else - let List.576 : {List I64, I64} = Struct {List.113, List.115}; - ret List.576; + let List.579 : {List I64, I64} = Struct {List.116, List.118}; + ret List.579; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.579 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.579; + let List.582 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Test.2 (Test.3): let Test.6 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/list_sort_asc.txt b/crates/compiler/test_mono/generated/list_sort_asc.txt index 426614413b8..0989f7cabc1 100644 --- a/crates/compiler/test_mono/generated/list_sort_asc.txt +++ b/crates/compiler/test_mono/generated/list_sort_asc.txt @@ -1,15 +1,15 @@ procedure List.28 (#Attr.2, #Attr.3): - let List.576 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; - ret List.576; + let List.579 : List I64 = lowlevel ListSortWith { xs: `#Attr.#arg1` } #Attr.2 Num.46 #Attr.3; + ret List.579; -procedure List.59 (List.330): - let List.575 : {} = Struct {}; - let List.574 : List I64 = CallByName List.28 List.330 List.575; - ret List.574; +procedure List.59 (List.333): + let List.578 : {} = Struct {}; + let List.577 : List I64 = CallByName List.28 List.333 List.578; + ret List.577; procedure Num.46 (#Attr.2, #Attr.3): - let Num.279 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U8 = lowlevel NumCompare #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.2 : List I64 = Array [4i64, 3i64, 2i64, 1i64]; diff --git a/crates/compiler/test_mono/generated/multiline_record_pattern.txt b/crates/compiler/test_mono/generated/multiline_record_pattern.txt index 1566d741057..baecd19d985 100644 --- a/crates/compiler/test_mono/generated/multiline_record_pattern.txt +++ b/crates/compiler/test_mono/generated/multiline_record_pattern.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Test.0 (): let Test.7 : I64 = 1i64; diff --git a/crates/compiler/test_mono/generated/nested_optional_field_with_binary_op.txt b/crates/compiler/test_mono/generated/nested_optional_field_with_binary_op.txt index 07d6ad3b213..617adedd422 100644 --- a/crates/compiler/test_mono/generated/nested_optional_field_with_binary_op.txt +++ b/crates/compiler/test_mono/generated/nested_optional_field_with_binary_op.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.17 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/nested_pattern_match.txt b/crates/compiler/test_mono/generated/nested_pattern_match.txt index a2708316625..32875666f6c 100644 --- a/crates/compiler/test_mono/generated/nested_pattern_match.txt +++ b/crates/compiler/test_mono/generated/nested_pattern_match.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.19 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt b/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt index d2de4a4424e..936b6203e1a 100644 --- a/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt +++ b/crates/compiler/test_mono/generated/num_width_gt_u8_layout_as_float.txt @@ -1,6 +1,6 @@ procedure Num.37 (#Attr.2, #Attr.3): - let Num.279 : Decimal = lowlevel NumDivFrac #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Decimal = lowlevel NumDivFrac #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.2 : Decimal = 1dec; diff --git a/crates/compiler/test_mono/generated/optional_field_with_binary_op.txt b/crates/compiler/test_mono/generated/optional_field_with_binary_op.txt index e0abf66bdca..4e2be037b44 100644 --- a/crates/compiler/test_mono/generated/optional_field_with_binary_op.txt +++ b/crates/compiler/test_mono/generated/optional_field_with_binary_op.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.5 : {} = Struct {}; diff --git a/crates/compiler/test_mono/generated/optional_when.txt b/crates/compiler/test_mono/generated/optional_when.txt index c1e0877034b..c1ceb98a6e3 100644 --- a/crates/compiler/test_mono/generated/optional_when.txt +++ b/crates/compiler/test_mono/generated/optional_when.txt @@ -1,6 +1,6 @@ procedure Num.21 (#Attr.2, #Attr.3): - let Num.281 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.283; procedure Test.1 (Test.6): let Test.21 : Int1 = false; diff --git a/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt b/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt index 1e74803e858..d93fcaf31ad 100644 --- a/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt +++ b/crates/compiler/test_mono/generated/polymorphic_expression_unification.txt @@ -3,8 +3,8 @@ procedure Bool.11 (#Attr.2, #Attr.3): ret Bool.23; procedure Str.3 (#Attr.2, #Attr.3): - let Str.233 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.233; + let Str.237 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.237; procedure Test.2 (Test.7): let Test.24 : Str = ".trace(\""; diff --git a/crates/compiler/test_mono/generated/quicksort_help.txt b/crates/compiler/test_mono/generated/quicksort_help.txt index 26912208f7d..5f2d3e63e84 100644 --- a/crates/compiler/test_mono/generated/quicksort_help.txt +++ b/crates/compiler/test_mono/generated/quicksort_help.txt @@ -1,14 +1,14 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Num.20 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.282; procedure Num.22 (#Attr.2, #Attr.3): - let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.283; procedure Test.1 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): joinpoint Test.12 Test.2 Test.3 Test.4: diff --git a/crates/compiler/test_mono/generated/quicksort_swap.txt b/crates/compiler/test_mono/generated/quicksort_swap.txt index 5a4478f672d..7976cae7753 100644 --- a/crates/compiler/test_mono/generated/quicksort_swap.txt +++ b/crates/compiler/test_mono/generated/quicksort_swap.txt @@ -1,47 +1,47 @@ -procedure List.2 (List.108, List.109): - let List.596 : U64 = CallByName List.6 List.108; - let List.593 : Int1 = CallByName Num.22 List.109 List.596; - if List.593 then - let List.595 : I64 = CallByName List.66 List.108 List.109; - dec List.108; - let List.594 : [C {}, C I64] = TagId(1) List.595; - ret List.594; +procedure List.2 (List.111, List.112): + let List.599 : U64 = CallByName List.6 List.111; + let List.596 : Int1 = CallByName Num.22 List.112 List.599; + if List.596 then + let List.598 : I64 = CallByName List.66 List.111 List.112; + dec List.111; + let List.597 : [C {}, C I64] = TagId(1) List.598; + ret List.597; else - dec List.108; - let List.592 : {} = Struct {}; - let List.591 : [C {}, C I64] = TagId(0) List.592; - ret List.591; + dec List.111; + let List.595 : {} = Struct {}; + let List.594 : [C {}, C I64] = TagId(0) List.595; + ret List.594; -procedure List.3 (List.116, List.117, List.118): - let List.583 : {List I64, I64} = CallByName List.64 List.116 List.117 List.118; - let List.582 : List I64 = StructAtIndex 0 List.583; - ret List.582; +procedure List.3 (List.119, List.120, List.121): + let List.586 : {List I64, I64} = CallByName List.64 List.119 List.120 List.121; + let List.585 : List I64 = StructAtIndex 0 List.586; + ret List.585; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; -procedure List.64 (List.113, List.114, List.115): - let List.580 : U64 = CallByName List.6 List.113; - let List.577 : Int1 = CallByName Num.22 List.114 List.580; - if List.577 then - let List.578 : {List I64, I64} = CallByName List.67 List.113 List.114 List.115; - ret List.578; +procedure List.64 (List.116, List.117, List.118): + let List.583 : U64 = CallByName List.6 List.116; + let List.580 : Int1 = CallByName Num.22 List.117 List.583; + if List.580 then + let List.581 : {List I64, I64} = CallByName List.67 List.116 List.117 List.118; + ret List.581; else - let List.576 : {List I64, I64} = Struct {List.113, List.115}; - ret List.576; + let List.579 : {List I64, I64} = Struct {List.116, List.118}; + ret List.579; procedure List.66 (#Attr.2, #Attr.3): - let List.589 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.589; + let List.592 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.592; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.579 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.579; + let List.582 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.283; procedure Test.1 (Test.2): let Test.28 : U64 = 0i64; diff --git a/crates/compiler/test_mono/generated/rb_tree_fbip.txt b/crates/compiler/test_mono/generated/rb_tree_fbip.txt index 57f8438abdf..2bfdeb60061 100644 --- a/crates/compiler/test_mono/generated/rb_tree_fbip.txt +++ b/crates/compiler/test_mono/generated/rb_tree_fbip.txt @@ -1,10 +1,10 @@ procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.24 (#Attr.2, #Attr.3): - let Num.280 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.282; procedure Test.3 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2): let #Derived_gen.4 : [C *self I64 *self I32 Int1, ] = NullPointer; diff --git a/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt index ff574612f54..0bf4a409e43 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_function_no_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.4): let Test.2 : I64 = StructAtIndex 0 Test.4; diff --git a/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt index 4e6ae59c9c1..63d2b8e07f8 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_function_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.4): let Test.2 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt index bcc459113af..f8ee86c57d1 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_let_no_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2): let Test.3 : I64 = StructAtIndex 0 Test.2; diff --git a/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt b/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt index 61016fa3aa6..d877cdf19c6 100644 --- a/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt +++ b/crates/compiler/test_mono/generated/record_optional_field_let_use_default.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2): let Test.3 : I64 = 10i64; diff --git a/crates/compiler/test_mono/generated/record_update.txt b/crates/compiler/test_mono/generated/record_update.txt index 9f41c20bf66..63de94d0d8a 100644 --- a/crates/compiler/test_mono/generated/record_update.txt +++ b/crates/compiler/test_mono/generated/record_update.txt @@ -1,29 +1,29 @@ -procedure List.3 (List.116, List.117, List.118): - let List.583 : {List U64, U64} = CallByName List.64 List.116 List.117 List.118; - let List.582 : List U64 = StructAtIndex 0 List.583; - ret List.582; +procedure List.3 (List.119, List.120, List.121): + let List.586 : {List U64, U64} = CallByName List.64 List.119 List.120 List.121; + let List.585 : List U64 = StructAtIndex 0 List.586; + ret List.585; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; -procedure List.64 (List.113, List.114, List.115): - let List.580 : U64 = CallByName List.6 List.113; - let List.577 : Int1 = CallByName Num.22 List.114 List.580; - if List.577 then - let List.578 : {List U64, U64} = CallByName List.67 List.113 List.114 List.115; - ret List.578; +procedure List.64 (List.116, List.117, List.118): + let List.583 : U64 = CallByName List.6 List.116; + let List.580 : Int1 = CallByName Num.22 List.117 List.583; + if List.580 then + let List.581 : {List U64, U64} = CallByName List.67 List.116 List.117 List.118; + ret List.581; else - let List.576 : {List U64, U64} = Struct {List.113, List.115}; - ret List.576; + let List.579 : {List U64, U64} = Struct {List.116, List.118}; + ret List.579; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.579 : {List U64, U64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.579; + let List.582 : {List U64, U64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.279 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2): let Test.6 : List U64 = StructAtIndex 0 Test.2; diff --git a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt index 43a40b2a337..85f788c9cb3 100644 --- a/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt +++ b/crates/compiler/test_mono/generated/recursive_call_capturing_function.txt @@ -3,8 +3,8 @@ procedure Bool.2 (): ret Bool.23; procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U32 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U32 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2): let Test.8 : U32 = 0i64; diff --git a/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt b/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt index f8267319e6a..3280874b7f7 100644 --- a/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt +++ b/crates/compiler/test_mono/generated/recursive_function_and_union_with_inference_hole.txt @@ -1,7 +1,7 @@ procedure List.5 (#Attr.2, #Attr.3): - let List.574 : List [C List *self] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; + let List.577 : List [C List *self] = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.2 #Attr.3; decref #Attr.2; - ret List.574; + ret List.577; procedure Test.2 (Test.5): let Test.6 : List [C List *self] = UnionAtIndex (Id 0) (Index 0) Test.5; diff --git a/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt b/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt index 2e9faa9d1bb..f679a2e5d04 100644 --- a/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt +++ b/crates/compiler/test_mono/generated/recursive_lambda_set_resolved_only_upon_specialization.txt @@ -3,12 +3,12 @@ procedure Bool.11 (#Attr.2, #Attr.3): ret Bool.23; procedure Num.20 (#Attr.2, #Attr.3): - let Num.280 : U8 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U8 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.282; procedure Num.21 (#Attr.2, #Attr.3): - let Num.279 : U8 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U8 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.11 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/recursively_build_effect.txt b/crates/compiler/test_mono/generated/recursively_build_effect.txt index a6bcf407a8b..87e8e672ac5 100644 --- a/crates/compiler/test_mono/generated/recursively_build_effect.txt +++ b/crates/compiler/test_mono/generated/recursively_build_effect.txt @@ -1,10 +1,10 @@ procedure Num.20 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.281; procedure Str.3 (#Attr.2, #Attr.3): - let Str.234 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.234; + let Str.238 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.238; procedure Test.11 (Test.29, #Attr.12): let Test.32 : {} = UnionAtIndex (Id 0) (Index 0) #Attr.12; diff --git a/crates/compiler/test_mono/generated/rigids.txt b/crates/compiler/test_mono/generated/rigids.txt index 99c1b2c6a2d..f125e1f5d02 100644 --- a/crates/compiler/test_mono/generated/rigids.txt +++ b/crates/compiler/test_mono/generated/rigids.txt @@ -1,47 +1,47 @@ -procedure List.2 (List.108, List.109): - let List.596 : U64 = CallByName List.6 List.108; - let List.593 : Int1 = CallByName Num.22 List.109 List.596; - if List.593 then - let List.595 : I64 = CallByName List.66 List.108 List.109; - dec List.108; - let List.594 : [C {}, C I64] = TagId(1) List.595; - ret List.594; +procedure List.2 (List.111, List.112): + let List.599 : U64 = CallByName List.6 List.111; + let List.596 : Int1 = CallByName Num.22 List.112 List.599; + if List.596 then + let List.598 : I64 = CallByName List.66 List.111 List.112; + dec List.111; + let List.597 : [C {}, C I64] = TagId(1) List.598; + ret List.597; else - dec List.108; - let List.592 : {} = Struct {}; - let List.591 : [C {}, C I64] = TagId(0) List.592; - ret List.591; + dec List.111; + let List.595 : {} = Struct {}; + let List.594 : [C {}, C I64] = TagId(0) List.595; + ret List.594; -procedure List.3 (List.116, List.117, List.118): - let List.583 : {List I64, I64} = CallByName List.64 List.116 List.117 List.118; - let List.582 : List I64 = StructAtIndex 0 List.583; - ret List.582; +procedure List.3 (List.119, List.120, List.121): + let List.586 : {List I64, I64} = CallByName List.64 List.119 List.120 List.121; + let List.585 : List I64 = StructAtIndex 0 List.586; + ret List.585; procedure List.6 (#Attr.2): - let List.581 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.581; + let List.584 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.584; -procedure List.64 (List.113, List.114, List.115): - let List.580 : U64 = CallByName List.6 List.113; - let List.577 : Int1 = CallByName Num.22 List.114 List.580; - if List.577 then - let List.578 : {List I64, I64} = CallByName List.67 List.113 List.114 List.115; - ret List.578; +procedure List.64 (List.116, List.117, List.118): + let List.583 : U64 = CallByName List.6 List.116; + let List.580 : Int1 = CallByName Num.22 List.117 List.583; + if List.580 then + let List.581 : {List I64, I64} = CallByName List.67 List.116 List.117 List.118; + ret List.581; else - let List.576 : {List I64, I64} = Struct {List.113, List.115}; - ret List.576; + let List.579 : {List I64, I64} = Struct {List.116, List.118}; + ret List.579; procedure List.66 (#Attr.2, #Attr.3): - let List.589 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.589; + let List.592 : I64 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.592; procedure List.67 (#Attr.2, #Attr.3, #Attr.4): - let List.579 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; - ret List.579; + let List.582 : {List I64, I64} = lowlevel ListReplaceUnsafe #Attr.2 #Attr.3 #Attr.4; + ret List.582; procedure Num.22 (#Attr.2, #Attr.3): - let Num.281 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.283; procedure Test.1 (Test.2, Test.3, Test.4): inc 2 Test.4; diff --git a/crates/compiler/test_mono/generated/specialize_after_match.txt b/crates/compiler/test_mono/generated/specialize_after_match.txt index 832e559bd4f..cb95b4a71a7 100644 --- a/crates/compiler/test_mono/generated/specialize_after_match.txt +++ b/crates/compiler/test_mono/generated/specialize_after_match.txt @@ -1,10 +1,10 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Num.24 (#Attr.2, #Attr.3): - let Num.281 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : Int1 = lowlevel NumGt #Attr.2 #Attr.3; + ret Num.283; procedure Test.2 (Test.9, Test.10): let Test.38 : U8 = 1i64; diff --git a/crates/compiler/test_mono/generated/specialize_closures.txt b/crates/compiler/test_mono/generated/specialize_closures.txt index 8998ad0832f..ad9a4917a7b 100644 --- a/crates/compiler/test_mono/generated/specialize_closures.txt +++ b/crates/compiler/test_mono/generated/specialize_closures.txt @@ -3,12 +3,12 @@ procedure Bool.2 (): ret Bool.24; procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Num.21 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2, Test.3): let Test.15 : U8 = GetTagId Test.2; diff --git a/crates/compiler/test_mono/generated/specialize_lowlevel.txt b/crates/compiler/test_mono/generated/specialize_lowlevel.txt index 5c288fc665b..d160decc952 100644 --- a/crates/compiler/test_mono/generated/specialize_lowlevel.txt +++ b/crates/compiler/test_mono/generated/specialize_lowlevel.txt @@ -3,12 +3,12 @@ procedure Bool.2 (): ret Bool.23; procedure Num.19 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.282; procedure Num.21 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumMul #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumMul #Attr.2 #Attr.3; + ret Num.281; procedure Test.6 (Test.8, #Attr.12): let Test.20 : I64 = UnionAtIndex (Id 0) (Index 0) #Attr.12; diff --git a/crates/compiler/test_mono/generated/tail_call_elimination.txt b/crates/compiler/test_mono/generated/tail_call_elimination.txt index f6e047d6cc5..a79c0883245 100644 --- a/crates/compiler/test_mono/generated/tail_call_elimination.txt +++ b/crates/compiler/test_mono/generated/tail_call_elimination.txt @@ -1,10 +1,10 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Num.20 (#Attr.2, #Attr.3): - let Num.280 : I64 = lowlevel NumSub #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : I64 = lowlevel NumSub #Attr.2 #Attr.3; + ret Num.282; procedure Test.1 (#Derived_gen.0, #Derived_gen.1): joinpoint Test.7 Test.2 Test.3: diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt index 44b28965f95..7dbffc622bc 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_types.txt @@ -2,108 +2,108 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName Test.213 Encode.99 Encode.101 Encode.107; - ret Encode.111; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName Test.213 Encode.101 Encode.103 Encode.109; + ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.115 : List U8 = CallByName Test.63 Encode.99 Encode.101 Encode.107; - ret Encode.115; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.117 : List U8 = CallByName Test.63 Encode.101 Encode.103 Encode.109; + ret Encode.117; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.116 : List U8 = CallByName Test.59 Encode.99 Encode.101 Encode.107; - ret Encode.116; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.118 : List U8 = CallByName Test.59 Encode.101 Encode.103 Encode.109; + ret Encode.118; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : {Str, Str} = CallByName Test.49 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : {Str, Str} = CallByName Test.49 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.600 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.600; + let List.603 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.603; -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; -procedure List.4 (List.124, List.125): - let List.596 : U64 = 1i64; - let List.595 : List U8 = CallByName List.70 List.124 List.596; - let List.594 : List U8 = CallByName List.71 List.595 List.125; - ret List.594; +procedure List.4 (List.127, List.128): + let List.599 : U64 = 1i64; + let List.598 : List U8 = CallByName List.70 List.127 List.599; + let List.597 : List U8 = CallByName List.71 List.598 List.128; + ret List.597; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.70 (#Attr.2, #Attr.3): - let List.590 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.590; + let List.593 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.593; procedure List.71 (#Attr.2, #Attr.3): - let List.588 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.588; + let List.591 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.591; procedure List.8 (#Attr.2, #Attr.3): - let List.598 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.598; - -procedure List.92 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : Str = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.66 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.601 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.601; + +procedure List.95 (#Derived_gen.9, #Derived_gen.10, #Derived_gen.11, #Derived_gen.12, #Derived_gen.13): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : Str = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.66 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.580 #Derived_gen.9 #Derived_gen.10 #Derived_gen.11 #Derived_gen.12 #Derived_gen.13; procedure Num.127 (#Attr.2): - let Num.280 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.280; + let Num.282 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.282; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.96 (#Attr.2): - let Num.279 : Str = lowlevel NumToStr #Attr.2; - ret Num.279; + let Num.281 : Str = lowlevel NumToStr #Attr.2; + ret Num.281; procedure Str.12 (#Attr.2): - let Str.233 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.233; + let Str.237 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.237; procedure Str.36 (#Attr.2): - let Str.234 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.234; + let Str.238 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.238; procedure Test.20 (Test.58): let Test.295 : Str = CallByName Encode.23 Test.58; diff --git a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt index 3fd9af2eeb3..2b2bdffa0bf 100644 --- a/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt +++ b/crates/compiler/test_mono/generated/unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unification_of_unifiable.txt @@ -34,168 +34,168 @@ procedure Bool.2 (): let Bool.23 : Int1 = true; ret Bool.23; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.23 (Encode.98): - ret Encode.98; +procedure Encode.23 (Encode.100): + ret Encode.100; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.111 : List U8 = CallByName Test.213 Encode.99 Encode.101 Encode.107; - ret Encode.111; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.113 : List U8 = CallByName Test.213 Encode.101 Encode.103 Encode.109; + ret Encode.113; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.115 : List U8 = CallByName Test.63 Encode.99 Encode.101 Encode.107; - ret Encode.115; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.117 : List U8 = CallByName Test.63 Encode.101 Encode.103 Encode.109; + ret Encode.117; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.117 : U8 = GetTagId Encode.107; - switch Encode.117: +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.119 : U8 = GetTagId Encode.109; + switch Encode.119: case 0: - let Encode.116 : List U8 = CallByName #Derived.2 Encode.99 Encode.101 Encode.107; - ret Encode.116; + let Encode.118 : List U8 = CallByName #Derived.2 Encode.101 Encode.103 Encode.109; + ret Encode.118; case 1: - let Encode.116 : List U8 = CallByName #Derived.7 Encode.99 Encode.101 Encode.107; - ret Encode.116; + let Encode.118 : List U8 = CallByName #Derived.7 Encode.101 Encode.103 Encode.109; + ret Encode.118; default: - let Encode.116 : List U8 = CallByName Test.59 Encode.99 Encode.101 Encode.107; - ret Encode.116; + let Encode.118 : List U8 = CallByName Test.59 Encode.101 Encode.103 Encode.109; + ret Encode.118; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.121 : List U8 = CallByName Test.63 Encode.99 Encode.101 Encode.107; - ret Encode.121; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.123 : List U8 = CallByName Test.63 Encode.101 Encode.103 Encode.109; + ret Encode.123; -procedure Encode.24 (Encode.99, Encode.107, Encode.101): - let Encode.124 : List U8 = CallByName Test.59 Encode.99 Encode.101 Encode.107; - ret Encode.124; +procedure Encode.24 (Encode.101, Encode.109, Encode.103): + let Encode.126 : List U8 = CallByName Test.59 Encode.101 Encode.103 Encode.109; + ret Encode.126; -procedure Encode.26 (Encode.105, Encode.106): - let Encode.109 : List U8 = Array []; - let Encode.110 : {{}, {}} = CallByName Test.49 Encode.105; - let Encode.108 : List U8 = CallByName Encode.24 Encode.109 Encode.110 Encode.106; - ret Encode.108; +procedure Encode.26 (Encode.107, Encode.108): + let Encode.111 : List U8 = Array []; + let Encode.112 : {{}, {}} = CallByName Test.49 Encode.107; + let Encode.110 : List U8 = CallByName Encode.24 Encode.111 Encode.112 Encode.108; + ret Encode.110; procedure List.13 (#Attr.2, #Attr.3): - let List.600 : List [C {}, C {}, C Str] = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.600; + let List.603 : List [C {}, C {}, C Str] = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.603; procedure List.13 (#Attr.2, #Attr.3): - let List.628 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; - ret List.628; - -procedure List.18 (List.160, List.161, List.162): - let List.575 : U64 = 0i64; - let List.576 : U64 = CallByName List.6 List.160; - let List.574 : List U8 = CallByName List.92 List.160 List.161 List.162 List.575 List.576; - ret List.574; - -procedure List.18 (List.160, List.161, List.162): - let List.602 : U64 = 0i64; - let List.603 : U64 = CallByName List.6 List.160; - let List.601 : List U8 = CallByName List.92 List.160 List.161 List.162 List.602 List.603; - ret List.601; - -procedure List.4 (List.124, List.125): - let List.623 : U64 = 1i64; - let List.622 : List U8 = CallByName List.70 List.124 List.623; - let List.621 : List U8 = CallByName List.71 List.622 List.125; - ret List.621; + let List.631 : List Str = lowlevel ListPrepend #Attr.2 #Attr.3; + ret List.631; + +procedure List.18 (List.163, List.164, List.165): + let List.578 : U64 = 0i64; + let List.579 : U64 = CallByName List.6 List.163; + let List.577 : List U8 = CallByName List.95 List.163 List.164 List.165 List.578 List.579; + ret List.577; + +procedure List.18 (List.163, List.164, List.165): + let List.605 : U64 = 0i64; + let List.606 : U64 = CallByName List.6 List.163; + let List.604 : List U8 = CallByName List.95 List.163 List.164 List.165 List.605 List.606; + ret List.604; + +procedure List.4 (List.127, List.128): + let List.626 : U64 = 1i64; + let List.625 : List U8 = CallByName List.70 List.127 List.626; + let List.624 : List U8 = CallByName List.71 List.625 List.128; + ret List.624; procedure List.6 (#Attr.2): - let List.599 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.599; + let List.602 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.602; procedure List.6 (#Attr.2): - let List.626 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.626; + let List.629 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.629; procedure List.66 (#Attr.2, #Attr.3): - let List.584 : [C {}, C {}, C Str] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.584; + let List.587 : [C {}, C {}, C Str] = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.587; procedure List.66 (#Attr.2, #Attr.3): - let List.611 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.611; + let List.614 : Str = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.614; procedure List.70 (#Attr.2, #Attr.3): - let List.617 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; - ret List.617; + let List.620 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3; + ret List.620; procedure List.71 (#Attr.2, #Attr.3): - let List.615 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; - ret List.615; + let List.618 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3; + ret List.618; procedure List.8 (#Attr.2, #Attr.3): - let List.625 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; - ret List.625; - -procedure List.92 (#Derived_gen.29, #Derived_gen.30, #Derived_gen.31, #Derived_gen.32, #Derived_gen.33): - joinpoint List.577 List.163 List.164 List.165 List.166 List.167: - let List.579 : Int1 = CallByName Num.22 List.166 List.167; - if List.579 then - let List.583 : [C {}, C {}, C Str] = CallByName List.66 List.163 List.166; - inc List.583; - let List.168 : List U8 = CallByName Test.66 List.164 List.583 List.165; - let List.582 : U64 = 1i64; - let List.581 : U64 = CallByName Num.51 List.166 List.582; - jump List.577 List.163 List.168 List.165 List.581 List.167; + let List.628 : List U8 = lowlevel ListConcat #Attr.2 #Attr.3; + ret List.628; + +procedure List.95 (#Derived_gen.26, #Derived_gen.27, #Derived_gen.28, #Derived_gen.29, #Derived_gen.30): + joinpoint List.580 List.166 List.167 List.168 List.169 List.170: + let List.582 : Int1 = CallByName Num.22 List.169 List.170; + if List.582 then + let List.586 : [C {}, C {}, C Str] = CallByName List.66 List.166 List.169; + inc List.586; + let List.171 : List U8 = CallByName Test.66 List.167 List.586 List.168; + let List.585 : U64 = 1i64; + let List.584 : U64 = CallByName Num.51 List.169 List.585; + jump List.580 List.166 List.171 List.168 List.584 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.577 #Derived_gen.29 #Derived_gen.30 #Derived_gen.31 #Derived_gen.32 #Derived_gen.33; - -procedure List.92 (#Derived_gen.52, #Derived_gen.53, #Derived_gen.54, #Derived_gen.55, #Derived_gen.56): - joinpoint List.604 List.163 List.164 List.165 List.166 List.167: - let List.606 : Int1 = CallByName Num.22 List.166 List.167; - if List.606 then - let List.610 : Str = CallByName List.66 List.163 List.166; - inc List.610; - let List.168 : List U8 = CallByName Test.66 List.164 List.610 List.165; - let List.609 : U64 = 1i64; - let List.608 : U64 = CallByName Num.51 List.166 List.609; - jump List.604 List.163 List.168 List.165 List.608 List.167; + jump List.580 #Derived_gen.26 #Derived_gen.27 #Derived_gen.28 #Derived_gen.29 #Derived_gen.30; + +procedure List.95 (#Derived_gen.34, #Derived_gen.35, #Derived_gen.36, #Derived_gen.37, #Derived_gen.38): + joinpoint List.607 List.166 List.167 List.168 List.169 List.170: + let List.609 : Int1 = CallByName Num.22 List.169 List.170; + if List.609 then + let List.613 : Str = CallByName List.66 List.166 List.169; + inc List.613; + let List.171 : List U8 = CallByName Test.66 List.167 List.613 List.168; + let List.612 : U64 = 1i64; + let List.611 : U64 = CallByName Num.51 List.169 List.612; + jump List.607 List.166 List.171 List.168 List.611 List.170; else - dec List.163; - ret List.164; + dec List.166; + ret List.167; in - jump List.604 #Derived_gen.52 #Derived_gen.53 #Derived_gen.54 #Derived_gen.55 #Derived_gen.56; + jump List.607 #Derived_gen.34 #Derived_gen.35 #Derived_gen.36 #Derived_gen.37 #Derived_gen.38; procedure Num.127 (#Attr.2): - let Num.284 : U8 = lowlevel NumIntCast #Attr.2; - ret Num.284; + let Num.286 : U8 = lowlevel NumIntCast #Attr.2; + ret Num.286; procedure Num.22 (#Attr.2, #Attr.3): - let Num.286 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.286; + let Num.288 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.288; procedure Num.51 (#Attr.2, #Attr.3): - let Num.285 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.285; + let Num.287 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.287; procedure Num.96 (#Attr.2): - let Num.283 : Str = lowlevel NumToStr #Attr.2; - ret Num.283; + let Num.285 : Str = lowlevel NumToStr #Attr.2; + ret Num.285; procedure Str.12 (#Attr.2): - let Str.236 : List U8 = lowlevel StrToUtf8 #Attr.2; - ret Str.236; + let Str.240 : List U8 = lowlevel StrToUtf8 #Attr.2; + ret Str.240; procedure Str.36 (#Attr.2): - let Str.237 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; - ret Str.237; + let Str.241 : U64 = lowlevel StrCountUtf8Bytes #Attr.2; + ret Str.241; procedure Test.20 (Test.58): let Test.299 : [C {}, C {}, C Str] = TagId(2) Test.58; diff --git a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt index a4208d3c6e3..b317746890d 100644 --- a/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt +++ b/crates/compiler/test_mono/generated/weakening_avoids_overspecialization.txt @@ -2,92 +2,92 @@ procedure Bool.11 (#Attr.2, #Attr.3): let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3; ret Bool.23; -procedure List.104 (List.488, List.489, List.490): - let List.592 : U64 = 0i64; - let List.593 : U64 = CallByName List.6 List.488; - let List.591 : [C U64, C U64] = CallByName List.80 List.488 List.489 List.490 List.592 List.593; - ret List.591; +procedure List.107 (List.491, List.492, List.493): + let List.595 : U64 = 0i64; + let List.596 : U64 = CallByName List.6 List.491; + let List.594 : [C U64, C U64] = CallByName List.80 List.491 List.492 List.493 List.595 List.596; + ret List.594; -procedure List.26 (List.201, List.202, List.203): - let List.585 : [C U64, C U64] = CallByName List.104 List.201 List.202 List.203; - let List.588 : U8 = 1i64; - let List.589 : U8 = GetTagId List.585; - let List.590 : Int1 = lowlevel Eq List.588 List.589; - if List.590 then - let List.204 : U64 = UnionAtIndex (Id 1) (Index 0) List.585; - ret List.204; +procedure List.26 (List.204, List.205, List.206): + let List.588 : [C U64, C U64] = CallByName List.107 List.204 List.205 List.206; + let List.591 : U8 = 1i64; + let List.592 : U8 = GetTagId List.588; + let List.593 : Int1 = lowlevel Eq List.591 List.592; + if List.593 then + let List.207 : U64 = UnionAtIndex (Id 1) (Index 0) List.588; + ret List.207; else - let List.205 : U64 = UnionAtIndex (Id 0) (Index 0) List.585; - ret List.205; + let List.208 : U64 = UnionAtIndex (Id 0) (Index 0) List.588; + ret List.208; -procedure List.38 (List.344, List.345): - let List.584 : U64 = CallByName List.6 List.344; - let List.346 : U64 = CallByName Num.77 List.584 List.345; - let List.574 : List U8 = CallByName List.43 List.344 List.346; - ret List.574; +procedure List.38 (List.347, List.348): + let List.587 : U64 = CallByName List.6 List.347; + let List.349 : U64 = CallByName Num.77 List.587 List.348; + let List.577 : List U8 = CallByName List.43 List.347 List.349; + ret List.577; -procedure List.43 (List.342, List.343): - let List.582 : U64 = CallByName List.6 List.342; - let List.581 : U64 = CallByName Num.77 List.582 List.343; - let List.576 : {U64, U64} = Struct {List.343, List.581}; - let List.575 : List U8 = CallByName List.49 List.342 List.576; - ret List.575; +procedure List.43 (List.345, List.346): + let List.585 : U64 = CallByName List.6 List.345; + let List.584 : U64 = CallByName Num.77 List.585 List.346; + let List.579 : {U64, U64} = Struct {List.346, List.584}; + let List.578 : List U8 = CallByName List.49 List.345 List.579; + ret List.578; -procedure List.49 (List.420, List.421): - let List.578 : U64 = StructAtIndex 1 List.421; - let List.579 : U64 = StructAtIndex 0 List.421; - let List.577 : List U8 = CallByName List.72 List.420 List.578 List.579; - ret List.577; +procedure List.49 (List.423, List.424): + let List.581 : U64 = StructAtIndex 1 List.424; + let List.582 : U64 = StructAtIndex 0 List.424; + let List.580 : List U8 = CallByName List.72 List.423 List.581 List.582; + ret List.580; procedure List.6 (#Attr.2): - let List.583 : U64 = lowlevel ListLenU64 #Attr.2; - ret List.583; + let List.586 : U64 = lowlevel ListLenU64 #Attr.2; + ret List.586; procedure List.66 (#Attr.2, #Attr.3): - let List.606 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; - ret List.606; + let List.609 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3; + ret List.609; procedure List.72 (#Attr.2, #Attr.3, #Attr.4): - let List.580 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; - ret List.580; + let List.583 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4; + ret List.583; procedure List.80 (#Derived_gen.0, #Derived_gen.1, #Derived_gen.2, #Derived_gen.3, #Derived_gen.4): - joinpoint List.594 List.491 List.492 List.493 List.494 List.495: - let List.596 : Int1 = CallByName Num.22 List.494 List.495; - if List.596 then - let List.605 : U8 = CallByName List.66 List.491 List.494; - let List.597 : [C U64, C U64] = CallByName Test.3 List.492 List.605; - let List.602 : U8 = 1i64; - let List.603 : U8 = GetTagId List.597; - let List.604 : Int1 = lowlevel Eq List.602 List.603; - if List.604 then - let List.496 : U64 = UnionAtIndex (Id 1) (Index 0) List.597; - let List.600 : U64 = 1i64; - let List.599 : U64 = CallByName Num.51 List.494 List.600; - jump List.594 List.491 List.496 List.493 List.599 List.495; + joinpoint List.597 List.494 List.495 List.496 List.497 List.498: + let List.599 : Int1 = CallByName Num.22 List.497 List.498; + if List.599 then + let List.608 : U8 = CallByName List.66 List.494 List.497; + let List.600 : [C U64, C U64] = CallByName Test.3 List.495 List.608; + let List.605 : U8 = 1i64; + let List.606 : U8 = GetTagId List.600; + let List.607 : Int1 = lowlevel Eq List.605 List.606; + if List.607 then + let List.499 : U64 = UnionAtIndex (Id 1) (Index 0) List.600; + let List.603 : U64 = 1i64; + let List.602 : U64 = CallByName Num.51 List.497 List.603; + jump List.597 List.494 List.499 List.496 List.602 List.498; else - dec List.491; - let List.497 : U64 = UnionAtIndex (Id 0) (Index 0) List.597; - let List.601 : [C U64, C U64] = TagId(0) List.497; - ret List.601; + dec List.494; + let List.500 : U64 = UnionAtIndex (Id 0) (Index 0) List.600; + let List.604 : [C U64, C U64] = TagId(0) List.500; + ret List.604; else - dec List.491; - let List.595 : [C U64, C U64] = TagId(1) List.492; - ret List.595; + dec List.494; + let List.598 : [C U64, C U64] = TagId(1) List.495; + ret List.598; in - jump List.594 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; + jump List.597 #Derived_gen.0 #Derived_gen.1 #Derived_gen.2 #Derived_gen.3 #Derived_gen.4; procedure Num.22 (#Attr.2, #Attr.3): - let Num.282 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; - ret Num.282; + let Num.284 : Int1 = lowlevel NumLt #Attr.2 #Attr.3; + ret Num.284; procedure Num.51 (#Attr.2, #Attr.3): - let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; - ret Num.281; + let Num.283 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; + ret Num.283; procedure Num.77 (#Attr.2, #Attr.3): - let Num.280 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; - ret Num.280; + let Num.282 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3; + ret Num.282; procedure Test.3 (Test.4, Test.12): let Test.13 : [C U64, C U64] = TagId(0) Test.4; diff --git a/crates/compiler/test_mono/generated/when_guard_appears_multiple_times_in_compiled_decision_tree_issue_5176.txt b/crates/compiler/test_mono/generated/when_guard_appears_multiple_times_in_compiled_decision_tree_issue_5176.txt index 8844496ae51..bdd8a6188c9 100644 --- a/crates/compiler/test_mono/generated/when_guard_appears_multiple_times_in_compiled_decision_tree_issue_5176.txt +++ b/crates/compiler/test_mono/generated/when_guard_appears_multiple_times_in_compiled_decision_tree_issue_5176.txt @@ -3,8 +3,8 @@ procedure Bool.2 (): ret Bool.25; procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : U8 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.1 (Test.2): joinpoint Test.12: diff --git a/crates/compiler/test_mono/generated/when_nested_maybe.txt b/crates/compiler/test_mono/generated/when_nested_maybe.txt index a2708316625..32875666f6c 100644 --- a/crates/compiler/test_mono/generated/when_nested_maybe.txt +++ b/crates/compiler/test_mono/generated/when_nested_maybe.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.19 : I64 = 41i64; diff --git a/crates/compiler/test_mono/generated/when_on_record.txt b/crates/compiler/test_mono/generated/when_on_record.txt index b890935ed74..9da43455497 100644 --- a/crates/compiler/test_mono/generated/when_on_record.txt +++ b/crates/compiler/test_mono/generated/when_on_record.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.5 : I64 = 2i64; diff --git a/crates/compiler/test_mono/generated/when_on_two_values.txt b/crates/compiler/test_mono/generated/when_on_two_values.txt index a98325d7d5a..742cb21af2d 100644 --- a/crates/compiler/test_mono/generated/when_on_two_values.txt +++ b/crates/compiler/test_mono/generated/when_on_two_values.txt @@ -1,6 +1,6 @@ procedure Num.19 (#Attr.2, #Attr.3): - let Num.279 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; - ret Num.279; + let Num.281 : I64 = lowlevel NumAdd #Attr.2 #Attr.3; + ret Num.281; procedure Test.0 (): let Test.15 : I64 = 3i64; diff --git a/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic.txt b/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic.txt index 0f4fc5de7a2..e06114e6721 100644 --- a/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic.txt +++ b/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic.txt @@ -8,46 +8,46 @@ main = 1 # -emit:mono -procedure Inspect.248 (Inspect.249): - let Inspect.313 : Str = ""; - let Inspect.312 : Str = CallByName Inspect.59 Inspect.249 Inspect.313; - ret Inspect.312; - -procedure Inspect.30 (Inspect.143): - ret Inspect.143; - -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; - -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; +procedure Inspect.252 (Inspect.253): + let Inspect.317 : Str = ""; + let Inspect.316 : Str = CallByName Inspect.63 Inspect.253 Inspect.317; + ret Inspect.316; + +procedure Inspect.30 (Inspect.147): + ret Inspect.147; + +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; + +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; + +procedure Inspect.48 (Inspect.299): + let Inspect.314 : {} = Struct {}; + let Inspect.313 : {} = CallByName Inspect.30 Inspect.314; + ret Inspect.313; + +procedure Inspect.5 (Inspect.150): + let Inspect.312 : {} = CallByName Inspect.48 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName Inspect.252 Inspect.308; ret Inspect.307; -procedure Inspect.44 (Inspect.295): - let Inspect.310 : {} = Struct {}; - let Inspect.309 : {} = CallByName Inspect.30 Inspect.310; - ret Inspect.309; - -procedure Inspect.5 (Inspect.146): - let Inspect.308 : {} = CallByName Inspect.44 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName Inspect.248 Inspect.304; - ret Inspect.303; - -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.315 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.315; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.319 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.319; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.0 (): let Test.4 : {} = Struct {}; diff --git a/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic_late.txt b/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic_late.txt index 6f9474cc635..75a10bd328b 100644 --- a/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic_late.txt +++ b/crates/compiler/uitest/tests/ability/specialize/inspect/opaque_automatic_late.txt @@ -11,46 +11,46 @@ main = late (@Op {}) # -emit:mono -procedure Inspect.248 (Inspect.249): - let Inspect.313 : Str = ""; - let Inspect.312 : Str = CallByName Inspect.59 Inspect.249 Inspect.313; - ret Inspect.312; +procedure Inspect.252 (Inspect.253): + let Inspect.317 : Str = ""; + let Inspect.316 : Str = CallByName Inspect.63 Inspect.253 Inspect.317; + ret Inspect.316; -procedure Inspect.30 (Inspect.143): - ret Inspect.143; +procedure Inspect.30 (Inspect.147): + ret Inspect.147; -procedure Inspect.33 (Inspect.148): - let Inspect.301 : Str = CallByName Inspect.5 Inspect.148; - let Inspect.300 : Str = CallByName Inspect.60 Inspect.301; - ret Inspect.300; +procedure Inspect.33 (Inspect.152): + let Inspect.305 : Str = CallByName Inspect.5 Inspect.152; + let Inspect.304 : Str = CallByName Inspect.64 Inspect.305; + ret Inspect.304; -procedure Inspect.35 (Inspect.297): - let Inspect.307 : Str = ""; - ret Inspect.307; +procedure Inspect.39 (Inspect.301): + let Inspect.311 : Str = ""; + ret Inspect.311; -procedure Inspect.44 (Inspect.295): - let Inspect.310 : {} = Struct {}; - let Inspect.309 : {} = CallByName Inspect.30 Inspect.310; - ret Inspect.309; +procedure Inspect.48 (Inspect.299): + let Inspect.314 : {} = Struct {}; + let Inspect.313 : {} = CallByName Inspect.30 Inspect.314; + ret Inspect.313; -procedure Inspect.5 (Inspect.146): - let Inspect.308 : {} = CallByName Inspect.44 Inspect.146; - let Inspect.305 : {} = Struct {}; - let Inspect.304 : Str = CallByName Inspect.35 Inspect.305; - let Inspect.303 : Str = CallByName Inspect.248 Inspect.304; - ret Inspect.303; +procedure Inspect.5 (Inspect.150): + let Inspect.312 : {} = CallByName Inspect.48 Inspect.150; + let Inspect.309 : {} = Struct {}; + let Inspect.308 : Str = CallByName Inspect.39 Inspect.309; + let Inspect.307 : Str = CallByName Inspect.252 Inspect.308; + ret Inspect.307; -procedure Inspect.59 (Inspect.296, Inspect.292): - let Inspect.315 : Str = CallByName Str.3 Inspect.296 Inspect.292; - dec Inspect.292; - ret Inspect.315; +procedure Inspect.63 (Inspect.300, Inspect.296): + let Inspect.319 : Str = CallByName Str.3 Inspect.300 Inspect.296; + dec Inspect.296; + ret Inspect.319; -procedure Inspect.60 (Inspect.298): - ret Inspect.298; +procedure Inspect.64 (Inspect.302): + ret Inspect.302; procedure Str.3 (#Attr.2, #Attr.3): - let Str.232 : Str = lowlevel StrConcat #Attr.2 #Attr.3; - ret Str.232; + let Str.236 : Str = lowlevel StrConcat #Attr.2 #Attr.3; + ret Str.236; procedure Test.2 (Test.3): let Test.4 : Str = CallByName Inspect.33 Test.3; diff --git a/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt b/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt index 3173fa4bcc7..3656f86edc0 100644 --- a/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt +++ b/crates/compiler/uitest/tests/ability/specialize/set_eq_issue_4761.txt @@ -8,5 +8,5 @@ main = s2 = Set.empty {} Bool.isEq s1 s1 && Bool.isEq s2 s2 -# ^^^^^^^^^ Set#Bool.isEq(25): Set Str, Set Str -[[Set.isEq(25)]]-> Bool -# ^^^^^^^^^ Set#Bool.isEq(25): Set U8, Set U8 -[[Set.isEq(25)]]-> Bool +# ^^^^^^^^^ Set#Bool.isEq(31): Set Str, Set Str -[[Set.isEq(31)]]-> Bool +# ^^^^^^^^^ Set#Bool.isEq(31): Set U8, Set U8 -[[Set.isEq(31)]]-> Bool diff --git a/crates/compiler/uitest/tests/specialize/record_update_between_modules.txt b/crates/compiler/uitest/tests/specialize/record_update_between_modules.txt index a126e60f373..f9164a7a8f7 100644 --- a/crates/compiler/uitest/tests/specialize/record_update_between_modules.txt +++ b/crates/compiler/uitest/tests/specialize/record_update_between_modules.txt @@ -23,10 +23,10 @@ procedure Dep.0 (): ret Dep.1; procedure Test.0 (): - let Test.5 : {Str, Str} = CallByName Dep.0; - let Test.2 : Str = StructAtIndex 0 Test.5; - let #Derived_gen.0 : Str = StructAtIndex 1 Test.5; + let Test.6 : {Str, Str} = CallByName Dep.0; + let Test.3 : Str = StructAtIndex 0 Test.6; + let #Derived_gen.0 : Str = StructAtIndex 1 Test.6; dec #Derived_gen.0; - let Test.4 : Str = "http://www.example.com"; - let Test.1 : {Str, Str} = Struct {Test.2, Test.4}; - ret Test.1; + let Test.5 : Str = "http://www.example.com"; + let Test.2 : {Str, Str} = Struct {Test.3, Test.5}; + ret Test.2; From 3c78b4fb860ee8ae76537716ea0b5e5c85329c84 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 11:40:37 -0400 Subject: [PATCH 039/203] functional changes --- crates/compiler/can/src/desugar.rs | 2 +- crates/compiler/can/src/suffixed.rs | 2 +- crates/compiler/fmt/src/def.rs | 51 ++++++++++++++++++++++++++--- crates/compiler/fmt/src/spaces.rs | 2 +- crates/compiler/parse/src/ast.rs | 2 +- crates/compiler/parse/src/expr.rs | 8 ++--- 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index a036b1b7669..d7d131bd93d 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -170,7 +170,7 @@ fn desugar_value_def<'a>( ext: None, }, )), - comment: None, + comment: &[], body_pattern: new_pat, body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path), } diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index b9299566d25..eed1849aa59 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -900,7 +900,7 @@ pub fn apply_task_await<'a>( ]), ), )), - comment: None, + comment: &[], body_pattern: arena.alloc(Loc::at( loc_pat.region, Pattern::Identifier { ident: new_ident }, diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 95c3ecdfb61..40fd59faae1 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -456,11 +456,7 @@ impl<'a> Formattable for ValueDef<'a> { } => { fmt_general_def(ann_pattern, buf, indent, ":", &ann_type.value, newlines); - if let Some(comment_str) = comment { - buf.push_str(" #"); - buf.spaces(1); - buf.push_str(comment_str.trim()); - } + fmt_annotated_body_comment(buf, indent, comment); buf.newline(); fmt_body(buf, &body_pattern.value, &body_expr.value, indent); @@ -586,6 +582,51 @@ pub fn fmt_defs(buf: &mut Buf, defs: &Defs, indent: u16) { defs.format(buf, indent); } +pub fn fmt_annotated_body_comment<'a>( + buf: &mut Buf, + indent: u16, + comment: &'a [roc_parse::ast::CommentOrNewline<'a>], +) { + let mut comment_iter = comment.iter(); + if let Some(comment_first) = comment_iter.next() { + match comment_first { + roc_parse::ast::CommentOrNewline::Newline => { + buf.newline(); + } + roc_parse::ast::CommentOrNewline::DocComment(comment_str) => { + buf.push_str(" # #"); + buf.spaces(1); + buf.push_str(comment_str.trim()); + } + roc_parse::ast::CommentOrNewline::LineComment(comment_str) => { + buf.push_str(" #"); + buf.spaces(1); + buf.push_str(comment_str.trim()); + } + } + + for comment_or_newline in comment_iter { + match comment_or_newline { + roc_parse::ast::CommentOrNewline::Newline => { + buf.newline(); + } + roc_parse::ast::CommentOrNewline::DocComment(comment_str) => { + buf.indent(indent); + buf.push_str("# #"); + buf.spaces(1); + buf.push_str(comment_str.trim()); + } + roc_parse::ast::CommentOrNewline::LineComment(comment_str) => { + buf.indent(indent); + buf.push_str("#"); + buf.spaces(1); + buf.push_str(comment_str.trim()); + } + } + } + } +} + pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) { // Check if this is an assignment into the unit value let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern { diff --git a/crates/compiler/fmt/src/spaces.rs b/crates/compiler/fmt/src/spaces.rs index 7bb057bedc4..5baa494271f 100644 --- a/crates/compiler/fmt/src/spaces.rs +++ b/crates/compiler/fmt/src/spaces.rs @@ -569,7 +569,7 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> { } => AnnotatedBody { ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), ann_type: arena.alloc(ann_type.remove_spaces(arena)), - comment: None, + comment: &[], body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), body_expr: arena.alloc(body_expr.remove_spaces(arena)), }, diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 81b3e408bfe..f7fadf0ada9 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -795,7 +795,7 @@ pub enum ValueDef<'a> { AnnotatedBody { ann_pattern: &'a Loc>, ann_type: &'a Loc>, - comment: Option<&'a str>, + comment: &'a [CommentOrNewline<'a>], body_pattern: &'a Loc>, body_expr: &'a Loc>, }, diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 072bae31366..063d3ce4b12 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1275,9 +1275,7 @@ macro_rules! join_ann_to_body { let value_def = ValueDef::AnnotatedBody { ann_pattern: $arena.alloc(*$ann_pattern), ann_type: $arena.alloc(*$ann_type), - comment: $spaces_before_current - .first() - .and_then($crate::ast::CommentOrNewline::comment_str), + comment: $spaces_before_current, body_pattern: $arena.alloc($loc_pattern), body_expr: *$arena.alloc($loc_def_expr), }; @@ -1311,9 +1309,7 @@ macro_rules! join_alias_to_body { let value_def = ValueDef::AnnotatedBody { ann_pattern: $arena.alloc(loc_ann_pattern), ann_type: $arena.alloc(*$ann_type), - comment: $spaces_before_current - .first() - .and_then($crate::ast::CommentOrNewline::comment_str), + comment: $spaces_before_current, body_pattern: $arena.alloc($loc_pattern), body_expr: *$arena.alloc($loc_def_expr), }; From 05f4062c69e5dd98388a7fd162f12a7832149b89 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 12:25:24 -0400 Subject: [PATCH 040/203] clippy --- crates/compiler/can/src/desugar.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index d7d131bd93d..542ff7f0b8a 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -92,7 +92,7 @@ fn desugar_value_def<'a>( } => AnnotatedBody { ann_pattern, ann_type, - comment: *comment, + comment, body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path), body_expr: desugar_expr(arena, body_expr, src, line_info, module_path), }, From db1c230f1fed854f0cf796bd09471039346eeee3 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 12:50:48 -0400 Subject: [PATCH 041/203] add test --- crates/compiler/test_syntax/tests/test_fmt.rs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 8691f589cbc..5fae1dc6c7a 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6223,13 +6223,35 @@ mod test_fmt { [first as last] | [first, last] -> first - + _ -> Not " ), ); } + #[test] + fn issue_6896() { + expr_formats_to( + indoc!( + r" + x : i32 + # comment + x = 1 + x + " + ), + indoc!( + r" + x : i32 + # comment + x = 1 + x + " + ), + ); + } + // this is a parse error atm // #[test] // fn multiline_apply() { From 4f32f4304827b176fd324c611593df96c4f7b2b1 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Mon, 8 Jul 2024 21:14:51 -0700 Subject: [PATCH 042/203] Implement block / indent based parsing ... and enforce that defs can only occur in blocks (or, inside parenthesized expressions) --- crates/cli/src/format.rs | 2 +- crates/compiler/can/src/desugar.rs | 4 - ...ffixed_tests__apply_argument_multiple.snap | 6 +- ...suffixed_tests__apply_argument_single.snap | 6 +- ...ffixed_tests__apply_argument_suffixed.snap | 6 +- ...ffixed_tests__apply_function_suffixed.snap | 6 +- ...ed__suffixed_tests__bang_in_pipe_root.snap | 6 +- .../test_suffixed__suffixed_tests__basic.snap | 6 +- ...ed__suffixed_tests__body_parens_apply.snap | 6 +- ...fixed__suffixed_tests__closure_simple.snap | 18 +- ...fixed_tests__closure_with_annotations.snap | 14 +- ...ed__suffixed_tests__closure_with_defs.snap | 6 +- ..._suffixed__suffixed_tests__dbg_simple.snap | 8 +- ...uffixed__suffixed_tests__dbg_stmt_arg.snap | 14 +- ...t_suffixed__suffixed_tests__deep_when.snap | 6 +- ..._suffixed_tests__defs_suffixed_middle.snap | 3 +- ...ixed__suffixed_tests__deps_final_expr.snap | 6 +- ...xed__suffixed_tests__expect_then_bang.snap | 61 +- ..._suffixed__suffixed_tests__if_complex.snap | 6 +- ...t_suffixed__suffixed_tests__if_simple.snap | 6 +- ...sts__last_stmt_not_top_level_suffixed.snap | 6 +- ...uffixed_tests__last_suffixed_multiple.snap | 6 +- ..._suffixed_tests__last_suffixed_single.snap | 10 +- ...xed__suffixed_tests__multi_defs_stmts.snap | 6 +- ...ed_tests__multiple_def_first_suffixed.snap | 10 +- ...ixed__suffixed_tests__multiple_suffix.snap | 6 +- ...fixed__suffixed_tests__nested_complex.snap | 6 +- ...suffixed__suffixed_tests__nested_defs.snap | 6 +- ...ffixed__suffixed_tests__nested_simple.snap | 16 +- ...uffixed__suffixed_tests__simple_pizza.snap | 20 +- ...ixed__suffixed_tests__trailing_binops.snap | 6 +- ...ed_tests__trailing_suffix_inside_when.snap | 6 +- ...ixed__suffixed_tests__type_annotation.snap | 18 +- ...uffixed__suffixed_tests__var_suffixes.snap | 18 +- ...ffixed__suffixed_tests__when_branches.snap | 6 +- ...suffixed__suffixed_tests__when_simple.snap | 6 +- crates/compiler/can/tests/test_can.rs | 4 +- crates/compiler/fmt/src/expr.rs | 10 +- crates/compiler/fmt/src/module.rs | 8 - crates/compiler/fmt/src/pattern.rs | 20 +- crates/compiler/fmt/src/spaces.rs | 865 +---- crates/compiler/load/tests/test_reporting.rs | 91 +- .../compiler/load_internal/tests/test_load.rs | 2 +- crates/compiler/module/src/called_via.rs | 60 +- crates/compiler/parse/src/ast.rs | 43 + crates/compiler/parse/src/blankspace.rs | 58 +- crates/compiler/parse/src/expr.rs | 2998 +++++++++-------- crates/compiler/parse/src/lib.rs | 1 + crates/compiler/parse/src/parser.rs | 10 +- crates/compiler/parse/src/remove_spaces.rs | 1749 ++++++++++ crates/compiler/parse/src/state.rs | 4 + crates/compiler/parse/src/test_helpers.rs | 6 +- crates/compiler/test_gen/src/gen_tags.rs | 4 +- crates/compiler/test_syntax/fuzz/Cargo.toml | 1 + .../fuzz/fuzz_targets/fuzz_expr.rs | 10 +- .../compiler/test_syntax/src/bin/minimize.rs | 23 + crates/compiler/test_syntax/src/lib.rs | 1 + crates/compiler/test_syntax/src/minimize.rs | 204 ++ .../compiler/test_syntax/src/test_helpers.rs | 27 +- ...ckpassing_after_annotation.expr.result-ast | 1 + .../backpassing_after_annotation.expr.roc | 2 + .../bound_variable.expr.formatted.roc | 0 .../fail/bound_variable.expr.result-ast | 1 + .../{pass => fail}/bound_variable.expr.roc | 0 ...f_missing_final_expression.expr.result-ast | 2 +- .../def_without_newline.expr.formatted.roc | 0 .../fail/def_without_newline.expr.result-ast | 1 + .../def_without_newline.expr.roc | 0 ...expression_indentation_end.expr.result-ast | 2 +- .../fail/if_missing_else.expr.result-ast | 2 +- .../fail/list_without_end.expr.result-ast | 2 +- ...fore_operator_with_defs.expr.formatted.roc | 0 ..._before_operator_with_defs.expr.result-ast | 1 + ...newline_before_operator_with_defs.expr.roc | 0 ...e_type_def_with_newline.expr.formatted.roc | 3 + ...aque_type_def_with_newline.expr.result-ast | 1 + .../opaque_type_def_with_newline.expr.roc | 0 .../pattern_in_parens_end.expr.result-ast | 2 +- ...attern_in_parens_end_comma.expr.result-ast | 2 +- ...tern_in_parens_indent_open.expr.result-ast | 2 +- .../pattern_in_parens_open.expr.result-ast | 2 +- .../fail/record_type_end.expr.result-ast | 2 +- .../fail/record_type_open.expr.result-ast | 2 +- .../record_type_open_indent.expr.result-ast | 2 +- .../fail/tag_union_end.expr.result-ast | 2 +- .../fail/tag_union_open.expr.result-ast | 2 +- .../fail/type_in_parens_end.expr.result-ast | 2 +- .../fail/type_in_parens_start.expr.result-ast | 2 +- ..._closure_pattern_in_parens.expr.result-ast | 2 +- ...mport_as_or_exposing.moduledefs.result-ast | 1 + ...ished_import_as_or_exposing.moduledefs.roc | 1 + .../fail/when_missing_arrow.expr.result-ast | 2 +- .../when_outdented_branch.expr.result-ast | 2 +- .../when_over_indented_int.expr.result-ast | 2 +- ...n_over_indented_underscore.expr.result-ast | 2 +- .../fail/where_type_variable.expr.result-ast | 2 +- .../fail/wild_case_arrow.expr.result-ast | 2 +- ...ed_pattern_field_access.expr.formatted.roc | 3 + ...ormed_pattern_field_access.expr.result-ast | 73 +- ...med_pattern_module_name.expr.formatted.roc | 3 + ...formed_pattern_module_name.expr.result-ast | 73 +- ...and_signature_is_multiline.expr.result-ast | 119 +- .../pass/ability_multi_line.expr.result-ast | 147 +- .../ability_single_line.expr.formatted.roc | 3 + .../pass/ability_single_line.expr.result-ast | 127 +- .../ability_two_in_a_row.expr.formatted.roc | 5 + .../pass/ability_two_in_a_row.expr.result-ast | 203 +- ...notated_record_destructure.expr.result-ast | 149 +- .../annotated_tag_destructure.expr.result-ast | 151 +- ...nnotated_tuple_destructure.expr.result-ast | 137 +- .../pass/basic_docs.expr.formatted.roc | 9 + .../snapshots/pass/basic_docs.expr.result-ast | 69 +- .../pass/bound_variable.expr.result-ast | 36 - .../call_with_newlines.expr.formatted.roc | 3 - .../pass/call_with_newlines.expr.result-ast | 25 - .../pass/call_with_newlines.expr.roc | 3 - .../pass/closure_in_binop.expr.formatted.roc | 3 - .../pass/closure_in_binop.expr.result-ast | 35 - .../snapshots/pass/closure_in_binop.expr.roc | 2 - ...refixed_indented_record.expr.formatted.roc | 8 + ...a_prefixed_indented_record.expr.result-ast | 130 + .../comma_prefixed_indented_record.expr.roc | 8 + ...comment_after_def.moduledefs.formatted.roc | 1 - ...omment_after_tag_in_def.expr.formatted.roc | 4 - .../comment_after_tag_in_def.expr.result-ast | 54 - .../pass/comment_after_tag_in_def.expr.roc | 4 - .../snapshots/pass/crash.expr.result-ast | 365 +- .../snapshots/pass/dbg.expr.formatted.roc | 3 + .../tests/snapshots/pass/dbg.expr.result-ast | 40 +- .../tests/snapshots/pass/dbg.expr.roc | 1 + .../pass/dbg_multiline.expr.result-ast | 47 +- .../pass/def_without_newline.expr.result-ast | 32 - ...dle_extra_indents.moduledefs.formatted.roc | 9 + ...middle_extra_indents.moduledefs.result-ast | 159 + ...ffixed_middle_extra_indents.moduledefs.roc | 9 + ...destructure_tag_assignment.expr.result-ast | 101 +- .../tests/snapshots/pass/docs.expr.result-ast | 69 +- .../empty_hosted_header.header.formatted.roc | 1 - .../empty_module_header.header.formatted.roc | 1 - .../empty_package_header.header.formatted.roc | 1 - ...empty_platform_header.header.formatted.roc | 1 - .../pass/empty_string.expr.formatted.roc | 1 + .../pass/empty_string.expr.result-ast | 11 +- .../snapshots/pass/expect.expr.formatted.roc | 3 +- .../snapshots/pass/expect.expr.result-ast | 66 +- .../pass/expect_defs.moduledefs.result-ast | 453 +++ .../snapshots/pass/expect_defs.moduledefs.roc | 20 + .../pass/expect_fx.moduledefs.formatted.roc | 2 - .../pass/expect_fx.moduledefs.result-ast | 5 +- .../expect_single_line.expr.formatted.roc | 7 + .../pass/expect_single_line.expr.result-ast | 91 + .../pass/expect_single_line.expr.roc | 7 + .../pass/extra_newline.expr.formatted.roc | 6 + .../pass/extra_newline.expr.result-ast | 80 + .../snapshots/pass/extra_newline.expr.roc | 5 + ...ion_with_tuple_ext_type.expr.formatted.roc | 4 + ...nction_with_tuple_ext_type.expr.result-ast | 165 +- ...unction_with_tuple_type.expr.formatted.roc | 4 + .../function_with_tuple_type.expr.result-ast | 171 +- .../snapshots/pass/if_def.expr.result-ast | 69 +- .../pass/import.moduledefs.result-ast | 6 +- .../import_from_package.moduledefs.result-ast | 13 +- .../import_with_alias.moduledefs.result-ast | 7 +- ...import_with_comments.moduledefs.result-ast | 31 +- .../import_with_exposed.moduledefs.result-ast | 11 +- .../import_with_params.moduledefs.result-ast | 13 +- ...after_multi_backpassing.expr.formatted.roc | 6 + ...ed_after_multi_backpassing.expr.result-ast | 196 ++ .../indented_after_multi_backpassing.expr.roc | 6 + .../pass/ingested_file.moduledefs.result-ast | 7 +- .../pass/inline_import.expr.formatted.roc | 4 + .../pass/inline_import.expr.result-ast | 195 +- .../inline_ingested_file.expr.formatted.roc | 3 + .../pass/inline_ingested_file.expr.result-ast | 120 +- ...ne_ingested_file_no_ann.expr.formatted.roc | 3 + ...nline_ingested_file_no_ann.expr.result-ast | 102 +- ..._closing_indent_not_enough.expr.result-ast | 169 +- ...e_indent_no_trailing_comma.expr.result-ast | 99 +- ...ent_with_trailing_comma.expr.formatted.roc | 5 + ...indent_with_trailing_comma.expr.result-ast | 107 +- .../list_pattern_weird_indent.expr.result-ast | 73 +- .../pass/list_patterns.expr.result-ast | 397 +-- .../minimal_app_header.header.formatted.roc | 1 - .../pass/mixed_docs.expr.formatted.roc | 7 + .../snapshots/pass/mixed_docs.expr.result-ast | 69 +- ...odule_def_newline.moduledefs.formatted.roc | 4 - .../module_def_newline.moduledefs.result-ast | 6 +- .../module_with_newline.header.formatted.roc | 1 - .../pass/multi_backpassing.expr.formatted.roc | 3 + .../pass/multi_backpassing.expr.result-ast | 81 +- ...i_backpassing_in_def.moduledefs.result-ast | 6 +- .../pass/multi_char_string.expr.formatted.roc | 1 + .../pass/multi_char_string.expr.result-ast | 11 +- ...inop_when_with_comments.expr.formatted.roc | 25 + ...e_binop_when_with_comments.expr.result-ast | 268 ++ ...ultiline_binop_when_with_comments.expr.roc | 25 + .../multiline_str_in_pat.expr.formatted.roc | 4 + .../pass/multiline_str_in_pat.expr.result-ast | 62 + .../pass/multiline_str_in_pat.expr.roc | 2 + .../pass/multiline_string.expr.result-ast | 165 +- ...ssing_no_newline_before.expr.formatted.roc | 7 - ...kpassing_no_newline_before.expr.result-ast | 97 - ...ted_backpassing_no_newline_before.expr.roc | 6 - ...ed_def_annotation.moduledefs.formatted.roc | 6 - ...ested_def_annotation.moduledefs.result-ast | 6 +- ...ted_def_without_newline.expr.formatted.roc | 4 - ...nested_def_without_newline.expr.result-ast | 64 - .../pass/nested_def_without_newline.expr.roc | 2 - .../snapshots/pass/nested_if.expr.result-ast | 75 +- .../newline_after_equals.expr.formatted.roc | 4 + .../pass/newline_after_equals.expr.result-ast | 77 +- ..._before_operator_with_defs.expr.result-ast | 49 - .../pass/newline_in_packages.full.result-ast | 3 +- .../number_literal_suffixes.expr.result-ast | 829 ++--- .../pass/old_app_header.full.formatted.roc | 1 + .../pass/old_app_header.full.result-ast | 4 +- .../pass/one_backpassing.expr.result-ast | 51 +- .../pass/one_char_string.expr.formatted.roc | 1 + .../pass/one_char_string.expr.result-ast | 11 +- .../snapshots/pass/one_def.expr.result-ast | 69 +- .../pass/one_spaced_def.expr.formatted.roc | 4 + .../pass/one_spaced_def.expr.result-ast | 69 +- ...ructure_first_item_in_body.expr.result-ast | 125 +- .../pass/opaque_has_abilities.expr.result-ast | 847 ++--- .../opaque_reference_expr.expr.formatted.roc | 1 + .../opaque_reference_expr.expr.result-ast | 9 +- ...nce_expr_with_arguments.expr.formatted.roc | 1 + ...erence_expr_with_arguments.expr.result-ast | 29 +- .../opaque_reference_pattern.expr.result-ast | 45 +- ...nce_pattern_with_arguments.expr.result-ast | 79 +- .../opaque_simple.moduledefs.formatted.roc | 1 - .../pass/opaque_simple.moduledefs.result-ast | 6 +- ...e_type_def_with_newline.expr.formatted.roc | 4 - ...aque_type_def_with_newline.expr.result-ast | 54 - ..._with_type_arguments.moduledefs.result-ast | 8 +- .../outdented_app_with_record.expr.result-ast | 135 +- .../outdented_colon_in_record.expr.result-ast | 121 +- .../pass/outdented_list.expr.result-ast | 107 +- .../pass/outdented_record.expr.result-ast | 115 +- ...in_value_def_annotation.expr.formatted.roc | 3 - ...ns_in_value_def_annotation.expr.result-ast | 52 - .../parens_in_value_def_annotation.expr.roc | 4 - .../parenthesized_type_def.expr.result-ast | 2 +- ...ized_type_def_space_before.expr.result-ast | 2 +- .../pass/parse_alias.expr.formatted.roc | 3 + .../pass/parse_alias.expr.result-ast | 105 +- .../pass/parse_as_ann.expr.formatted.roc | 3 + .../pass/parse_as_ann.expr.result-ast | 113 +- .../pass/pattern_as.expr.formatted.roc | 2 + .../snapshots/pass/pattern_as.expr.result-ast | 55 +- .../pattern_as_list_rest.expr.formatted.roc | 2 + .../pass/pattern_as_list_rest.expr.result-ast | 69 +- .../pass/pattern_as_spaces.expr.formatted.roc | 4 + .../pass/pattern_as_spaces.expr.result-ast | 73 +- ...ttern_with_space_in_parens.expr.result-ast | 139 +- .../snapshots/pass/plus_if.expr.formatted.roc | 1 + .../snapshots/pass/plus_if.expr.result-ast | 39 +- .../snapshots/pass/plus_when.expr.result-ast | 95 +- .../pass/provides_type.header.formatted.roc | 1 - .../record_destructure_def.expr.formatted.roc | 5 + .../record_destructure_def.expr.result-ast | 109 +- .../record_func_type_decl.expr.result-ast | 205 +- ...cord_type_with_function.expr.formatted.roc | 3 + .../record_type_with_function.expr.result-ast | 163 +- .../pass/separate_defs.moduledefs.result-ast | 137 + .../pass/separate_defs.moduledefs.roc | 7 + .../pass/space_before_colon.full.result-ast | 3 +- ...alone_module_defs.moduledefs.formatted.roc | 7 - ...ffixed_multiple_defs.moduledefs.result-ast | 2 +- .../pass/suffixed_one_def.full.formatted.roc | 2 + .../pass/suffixed_one_def.full.result-ast | 99 +- .../suffixed_optional_last.full.result-ast | 3 +- .../pass/two_backpassing.expr.result-ast | 77 +- .../pass/two_branch_when.expr.result-ast | 77 +- .../pass/two_spaced_def.expr.formatted.roc | 5 + .../pass/two_spaced_def.expr.result-ast | 95 +- .../underscore_backpassing.expr.result-ast | 51 +- ...core_in_assignment_pattern.expr.result-ast | 401 +-- .../pass/when_if_guard.expr.formatted.roc | 9 + .../pass/when_if_guard.expr.result-ast | 113 +- .../pass/when_in_parens.expr.result-ast | 53 +- .../when_in_parens_indented.expr.result-ast | 55 +- ..._with_alternative_patterns.expr.result-ast | 137 +- ..._with_function_application.expr.result-ast | 87 +- ...when_with_negative_numbers.expr.result-ast | 73 +- .../pass/when_with_numbers.expr.result-ast | 73 +- .../pass/when_with_records.expr.result-ast | 87 +- .../when_with_tuple_in_record.expr.result-ast | 163 +- .../pass/when_with_tuples.expr.result-ast | 123 +- .../where_clause_function.expr.formatted.roc | 3 + .../where_clause_function.expr.result-ast | 117 +- ...e_multiple_bound_abilities.expr.result-ast | 279 +- ...ere_clause_multiple_has.expr.formatted.roc | 3 + .../where_clause_multiple_has.expr.result-ast | 157 +- ...ltiple_has_across_newlines.expr.result-ast | 177 +- ...ere_clause_non_function.expr.formatted.roc | 3 + .../where_clause_non_function.expr.result-ast | 99 +- .../where_clause_on_newline.expr.result-ast | 117 +- .../pass/where_ident.expr.result-ast | 123 +- crates/compiler/test_syntax/tests/test_fmt.rs | 83 +- .../test_syntax/tests/test_snapshots.rs | 33 +- crates/repl_ui/src/lib.rs | 42 +- crates/repl_ui/src/repl_state.rs | 497 ++- crates/reporting/src/error/parse.rs | 23 + 304 files changed, 11880 insertions(+), 8706 deletions(-) create mode 100644 crates/compiler/parse/src/remove_spaces.rs create mode 100644 crates/compiler/test_syntax/src/bin/minimize.rs create mode 100644 crates/compiler/test_syntax/src/minimize.rs create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/bound_variable.expr.formatted.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/bound_variable.expr.roc (100%) rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/def_without_newline.expr.formatted.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/def_without_newline.expr.roc (100%) rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/newline_before_operator_with_defs.expr.formatted.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/newline_before_operator_with_defs.expr.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/opaque_type_def_with_newline.expr.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index a5a56903aa5..7d33833fb4c 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -6,9 +6,9 @@ use bumpalo::Bump; use roc_error_macros::{internal_error, user_error}; use roc_fmt::def::fmt_defs; use roc_fmt::module::fmt_module; -use roc_fmt::spaces::RemoveSpaces; use roc_fmt::{Ast, Buf}; use roc_parse::module::parse_module_defs; +use roc_parse::remove_spaces::RemoveSpaces; use roc_parse::{module, parser::SyntaxError, state::State}; #[derive(Copy, Clone, Debug)] diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index a036b1b7669..2ab9a5ec9c3 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -1361,10 +1361,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) { And => (ModuleName::BOOL, "and"), Or => (ModuleName::BOOL, "or"), Pizza => unreachable!("Cannot desugar the |> operator"), - Assignment => unreachable!("Cannot desugar the = operator"), - IsAliasType => unreachable!("Cannot desugar the : operator"), - IsOpaqueType => unreachable!("Cannot desugar the := operator"), - Backpassing => unreachable!("Cannot desugar the <- operator"), } } diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap index d5f94a4db28..8b9089316eb 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap index c121a190255..faf24d92ad0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap index 2828856b170..e314e29d1cc 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap index d72b00aad3f..41525566373 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap index 62aaa64c9f0..3806e86373d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index c78f101cc81..7f5bb962427 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap index 247b3cb142f..217f69856ce 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index 4ed25bf707f..f5ee75bfe53 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -73,22 +75,22 @@ Defs { type_defs: [], value_defs: [ AnnotatedBody { - ann_pattern: @31-42 Identifier { + ann_pattern: @31-43 Identifier { ident: "#!0_stmt", }, - ann_type: @31-42 Apply( + ann_type: @31-43 Apply( "", "Task", [ - @31-42 Record { + @31-43 Record { fields: [], ext: None, }, - @31-42 Inferred, + @31-43 Inferred, ], ), comment: None, - body_pattern: @31-42 Identifier { + body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, body_expr: @31-42 Apply( @@ -116,7 +118,7 @@ Defs { ), @31-42 Closure( [ - @31-42 Underscore( + @31-43 Underscore( "#!stmt", ), ], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index c88b75ab111..36a6dc4c3a1 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -76,7 +78,7 @@ Defs { Index(2147483648), ], regions: [ - @78-91, + @82-91, ], space_before: [ Slice(start = 0, length = 0), @@ -113,8 +115,8 @@ Defs { body_pattern: @78-79 Identifier { ident: "#!0_expr", }, - body_expr: @78-91 Apply( - @78-91 Var { + body_expr: @82-91 Apply( + @82-91 Var { module_name: "", ident: "line", }, @@ -129,7 +131,7 @@ Defs { }, ], }, - @78-91 Var { + @82-91 Var { module_name: "", ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index 490fddac5a7..ceb0ca47ec0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap index ad5037f246e..5b606704d04 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -38,7 +40,7 @@ Defs { ident: "foo", }, ], - @29-49 LowLevelDbg( + @29-36 LowLevelDbg( ( "test.roc:3", " ", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap index d3918ce09bb..4da8bb363c8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap @@ -13,17 +13,19 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-4 Identifier { ident: "main", }, - @11-24 Apply( - @11-24 Var { + @11-17 Apply( + @11-17 Var { module_name: "Task", ident: "await", }, @@ -32,13 +34,13 @@ Defs { module_name: "", ident: "a", }, - @11-24 Closure( + @11-17 Closure( [ @15-17 Identifier { ident: "#!0_arg", }, ], - @11-24 LowLevelDbg( + @11-17 LowLevelDbg( ( "test.roc:2", "in", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap index 31dfae24dba..421aa22f3b8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index a07119b6c8a..79dd90e6d85 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -17,11 +17,12 @@ Defs { ], space_after: [ Slice(start = 0, length = 0), - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap index 326b127051e..bafdfd47ec8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap index 5c38b6513c6..e7fa4402ef5 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap @@ -13,33 +13,56 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-4 Identifier { ident: "main", }, - @11-31 Expect( - @18-24 Apply( - @20-22 Var { - module_name: "Bool", - ident: "isEq", - }, - [ - @18-19 Num( - "1", - ), - @23-24 Num( - "2", - ), + @11-31 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @11-24, + ], + space_before: [ + Slice(start = 0, length = 0), ], - BinOp( - Equals, - ), - ), + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Expect { + condition: @18-24 Apply( + @20-22 Var { + module_name: "Bool", + ident: "isEq", + }, + [ + @18-19 Num( + "1", + ), + @23-24 Num( + "2", + ), + ], + BinOp( + Equals, + ), + ), + preceding_comment: @11-11, + }, + ], + }, @29-31 Var { module_name: "", ident: "x", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index 9e62fed3526..68c040cfad0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap index 02592ffa7b8..cdf56fb5bbc 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap index 8af15de1a89..d7f364a77d4 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index 8a090182841..59355d640ea 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap index 1d6cde10dae..dd4437e8317 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap @@ -13,17 +13,19 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-4 Identifier { ident: "main", }, - @0-26 Apply( - @0-26 Var { + @7-26 Apply( + @7-26 Var { module_name: "", ident: "foo", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 3f9caf99ca0..4245226948b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap index 47d204a6c09..cce6f34b263 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -57,8 +59,8 @@ Defs { ident: "await", }, [ - @29-41 Apply( - @29-41 Var { + @33-41 Apply( + @33-41 Var { module_name: "", ident: "foo", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index 4e2c7cb925d..56ecae8ecb6 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap index f9a595f7f96..90fbd9e6642 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap index e06ae062725..e077450d599 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap index b670cca0e45..8ce7be33813 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap @@ -13,17 +13,19 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-3 Identifier { ident: "run", }, - @0-22 Apply( - @0-22 Var { + @6-22 Apply( + @6-22 Var { module_name: "Task", ident: "await", }, @@ -32,14 +34,14 @@ Defs { module_name: "", ident: "nextMsg", }, - @0-22 Closure( + @6-22 Closure( [ Identifier { ident: "#!0_arg", }, ], - @0-22 Apply( - @0-22 Var { + @6-22 Apply( + @6-22 Var { module_name: "", ident: "line", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 5bf0ebd4e0c..d734a3c20ca 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -28,7 +30,7 @@ Defs { ident: "await", }, [ - @26-56 Defs( + @11-56 Defs( Defs { tags: [ Index(2147483648), @@ -46,22 +48,22 @@ Defs { type_defs: [], value_defs: [ AnnotatedBody { - ann_pattern: @26-56 Identifier { + ann_pattern: @11-57 Identifier { ident: "#!0_stmt", }, - ann_type: @26-56 Apply( + ann_type: @11-57 Apply( "", "Task", [ - @26-56 Record { + @11-57 Record { fields: [], ext: None, }, - @26-56 Inferred, + @11-57 Inferred, ], ), comment: None, - body_pattern: @26-56 Identifier { + body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, body_expr: @11-56 Apply( @@ -106,7 +108,7 @@ Defs { ), @11-56 Closure( [ - @26-56 Underscore( + @11-57 Underscore( "#!stmt", ), ], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index 52dbd271583..4e35c89c3d2 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap index da7e5a73e1e..e987494e705 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index e53888deea0..6ce8b488d9b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -28,8 +30,8 @@ Defs { ident: "x", }, ], - @24-30 Apply( - @24-30 Var { + @28-30 Apply( + @28-30 Var { module_name: "Task", ident: "await", }, @@ -40,7 +42,7 @@ Defs { Index(2147483648), ], regions: [ - @24-30, + @28-30, ], space_before: [ Slice(start = 0, length = 0), @@ -71,19 +73,19 @@ Defs { body_pattern: @24-25 Identifier { ident: "#!0_expr", }, - body_expr: @24-30 Var { + body_expr: @28-30 Var { module_name: "", ident: "x", }, }, ], }, - @24-30 Var { + @28-30 Var { module_name: "", ident: "#!0_expr", }, ), - @24-30 Closure( + @28-30 Closure( [ @24-25 Identifier { ident: "r", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap index 89099ba13b3..2791e5b3195 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -44,27 +46,27 @@ Defs { ident: "await", }, [ - @24-33 Var { + @28-33 Var { module_name: "", ident: "bar", }, @15-19 Closure( [ - @24-33 Identifier { + @28-33 Identifier { ident: "#!0_arg", }, ], - @24-33 Apply( - @24-33 Var { + @28-33 Apply( + @28-33 Var { module_name: "Task", ident: "await", }, [ - @24-33 Var { + @28-33 Var { module_name: "", ident: "#!0_arg", }, - @24-33 Closure( + @28-33 Closure( [ @24-25 Identifier { ident: "b", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index c8ef89774fa..def55dfc301 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap index 443dc9d0651..f803d115a3d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/test_can.rs b/crates/compiler/can/tests/test_can.rs index 429924b43b2..724d1f24b1a 100644 --- a/crates/compiler/can/tests/test_can.rs +++ b/crates/compiler/can/tests/test_can.rs @@ -382,7 +382,7 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 0); + assert_eq!(problems, Vec::new()); } #[test] @@ -399,7 +399,7 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 0); + assert_eq!(problems, Vec::new()); } #[test] diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 2f42225f00c..7793bd125d8 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -543,7 +543,7 @@ impl<'a> Formattable for Expr<'a> { } } -fn is_str_multiline(literal: &StrLiteral) -> bool { +pub fn is_str_multiline(literal: &StrLiteral) -> bool { use roc_parse::ast::StrLiteral::*; match literal { @@ -671,10 +671,6 @@ fn push_op(buf: &mut Buf, op: BinOp) { called_via::BinOp::And => buf.push_str("&&"), called_via::BinOp::Or => buf.push_str("||"), called_via::BinOp::Pizza => buf.push_str("|>"), - called_via::BinOp::Assignment => unreachable!(), - called_via::BinOp::IsAliasType => unreachable!(), - called_via::BinOp::IsOpaqueType => unreachable!(), - called_via::BinOp::Backpassing => unreachable!(), } } @@ -1708,10 +1704,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool { | BinOp::And | BinOp::Or | BinOp::Pizza => true, - BinOp::Assignment - | BinOp::IsAliasType - | BinOp::IsOpaqueType - | BinOp::Backpassing => false, }) } Expr::If(_, _) => true, diff --git a/crates/compiler/fmt/src/module.rs b/crates/compiler/fmt/src/module.rs index fc0db50567d..76568f85af0 100644 --- a/crates/compiler/fmt/src/module.rs +++ b/crates/compiler/fmt/src/module.rs @@ -3,10 +3,8 @@ use std::cmp::max; use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens}; use crate::collection::{fmt_collection, Braces}; use crate::expr::fmt_str_literal; -use crate::spaces::RemoveSpaces; use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT}; use crate::Buf; -use bumpalo::Bump; use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces}; use roc_parse::header::{ AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, @@ -58,12 +56,6 @@ macro_rules! keywords { buf.push_str($name::KEYWORD); } } - - impl<'a> RemoveSpaces<'a> for $name { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } - } )* } } diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index d18cabd383a..015c739a589 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -1,5 +1,5 @@ use crate::annotation::{Formattable, Newlines, Parens}; -use crate::expr::{fmt_str_literal, format_sq_literal}; +use crate::expr::{fmt_str_literal, format_sq_literal, is_str_multiline}; use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT}; use crate::Buf; use roc_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs}; @@ -48,7 +48,7 @@ impl<'a> Formattable for Pattern<'a> { pattern ); - spaces.iter().any(|s| s.is_comment()) + spaces.iter().any(|s| s.is_comment()) || pattern.is_multiline() } Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()), @@ -63,15 +63,17 @@ impl<'a> Formattable for Pattern<'a> { list_rest_spaces.iter().any(|s| s.is_comment()) || pattern_as.is_multiline() } }, + Pattern::StrLiteral(literal) => is_str_multiline(literal), + Pattern::Apply(pat, args) => { + pat.is_multiline() || args.iter().any(|a| a.is_multiline()) + } Pattern::Identifier { .. } | Pattern::Tag(_) | Pattern::OpaqueRef(_) - | Pattern::Apply(_, _) | Pattern::NumLiteral(..) | Pattern::NonBase10Literal { .. } | Pattern::FloatLiteral(..) - | Pattern::StrLiteral(_) | Pattern::SingleQuote(_) | Pattern::Underscore(_) | Pattern::Malformed(_) @@ -100,7 +102,13 @@ impl<'a> Formattable for Pattern<'a> { buf.indent(indent); // Sometimes, an Apply pattern needs parens around it. // In particular when an Apply's argument is itself an Apply (> 0) arguments - let parens = !loc_arg_patterns.is_empty() && parens == Parens::InApply; + let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply); + + let indent_more = if self.is_multiline() { + indent + INDENT + } else { + indent + }; if parens { buf.push('('); @@ -110,7 +118,7 @@ impl<'a> Formattable for Pattern<'a> { for loc_arg in loc_arg_patterns.iter() { buf.spaces(1); - loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent); + loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent_more); } if parens { diff --git a/crates/compiler/fmt/src/spaces.rs b/crates/compiler/fmt/src/spaces.rs index 7bb057bedc4..e794a3c67a9 100644 --- a/crates/compiler/fmt/src/spaces.rs +++ b/crates/compiler/fmt/src/spaces.rs @@ -1,23 +1,5 @@ -use bumpalo::collections::vec::Vec; use bumpalo::Bump; -use roc_module::called_via::{BinOp, UnaryOp}; -use roc_parse::{ - ast::{ - AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, - Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, - ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, - IngestedFileImport, Module, ModuleImport, ModuleImportParams, OldRecordBuilderField, - Pattern, PatternAs, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, - TypeHeader, ValueDef, WhenBranch, - }, - header::{ - AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName, - ModuleParams, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, - ProvidesTo, To, TypedIdent, - }, - ident::{BadIdent, UppercaseIdent}, -}; -use roc_region::all::{Loc, Position, Region}; +use roc_parse::{ast::CommentOrNewline, remove_spaces::RemoveSpaces}; use crate::{Ast, Buf}; @@ -211,20 +193,6 @@ fn fmt_docs(buf: &mut Buf, docs: &str) { buf.push_str(docs.trim_end()); } -/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting. -/// -/// Currently this consists of: -/// * Removing newlines -/// * Removing comments -/// * Removing parens in Exprs -/// -/// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting) -/// - but there are currently several bugs where they're _not_ preserved. -/// TODO: ensure formatting retains comments -pub trait RemoveSpaces<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self; -} - impl<'a> RemoveSpaces<'a> for Ast<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { Ast { @@ -233,834 +201,3 @@ impl<'a> RemoveSpaces<'a> for Ast<'a> { } } } - -impl<'a> RemoveSpaces<'a> for Defs<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let mut defs = self.clone(); - - defs.spaces.clear(); - defs.space_before.clear(); - defs.space_after.clear(); - - for type_def in defs.type_defs.iter_mut() { - *type_def = type_def.remove_spaces(arena); - } - - for value_def in defs.value_defs.iter_mut() { - *value_def = value_def.remove_spaces(arena); - } - - for region_def in defs.regions.iter_mut() { - *region_def = region_def.remove_spaces(arena); - } - - defs - } -} - -impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - Spaces { - before: &[], - item: self.item.remove_spaces(arena), - after: &[], - } - } -} - -impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - KeywordItem { - keyword: self.keyword.remove_spaces(arena), - item: self.item.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ProvidesTo { - provides_keyword: self.provides_keyword.remove_spaces(arena), - entries: self.entries.remove_spaces(arena), - types: self.types.remove_spaces(arena), - to_keyword: self.to_keyword.remove_spaces(arena), - to: self.to.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for Module<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let header = match &self.header { - Header::Module(header) => Header::Module(ModuleHeader { - after_keyword: &[], - params: header.params.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - interface_imports: header.interface_imports.remove_spaces(arena), - }), - Header::App(header) => Header::App(AppHeader { - before_provides: &[], - provides: header.provides.remove_spaces(arena), - before_packages: &[], - packages: header.packages.remove_spaces(arena), - old_imports: header.old_imports.remove_spaces(arena), - old_provides_to_new_package: header - .old_provides_to_new_package - .remove_spaces(arena), - }), - Header::Package(header) => Header::Package(PackageHeader { - before_exposes: &[], - exposes: header.exposes.remove_spaces(arena), - before_packages: &[], - packages: header.packages.remove_spaces(arena), - }), - Header::Platform(header) => Header::Platform(PlatformHeader { - before_name: &[], - name: header.name.remove_spaces(arena), - requires: header.requires.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - packages: header.packages.remove_spaces(arena), - imports: header.imports.remove_spaces(arena), - provides: header.provides.remove_spaces(arena), - }), - Header::Hosted(header) => Header::Hosted(HostedHeader { - before_name: &[], - name: header.name.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - imports: header.imports.remove_spaces(arena), - generates: header.generates.remove_spaces(arena), - generates_with: header.generates_with.remove_spaces(arena), - }), - }; - Module { - comments: &[], - header, - } - } -} - -impl<'a> RemoveSpaces<'a> for ModuleParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ModuleParams { - params: self.params.remove_spaces(arena), - before_arrow: &[], - after_arrow: &[], - } - } -} - -impl<'a> RemoveSpaces<'a> for Region { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - Region::zero() - } -} - -impl<'a> RemoveSpaces<'a> for &'a str { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - self - } -} - -impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)), - Spaced::SpaceBefore(a, _) => a.remove_spaces(arena), - Spaced::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ExposedName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for ModuleName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for PackageName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for To<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - To::ExistingPackage(a) => To::ExistingPackage(a), - To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)), - } - } -} - -impl<'a> RemoveSpaces<'a> for TypedIdent<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - TypedIdent { - ident: self.ident.remove_spaces(arena), - spaces_before_colon: &[], - ann: self.ann.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - PlatformRequires { - rigids: self.rigids.remove_spaces(arena), - signature: self.signature.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for PackageEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - PackageEntry { - shorthand: self.shorthand, - spaces_after_shorthand: &[], - platform_marker: match self.platform_marker { - Some(_) => Some(&[]), - None => None, - }, - package_name: self.package_name.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)), - ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)), - ImportsEntry::IngestedFile(a, b) => { - ImportsEntry::IngestedFile(a, b.remove_spaces(arena)) - } - } - } -} - -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - self.as_ref().map(|a| a.remove_spaces(arena)) - } -} - -impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let res = self.value.remove_spaces(arena); - Loc::at(Region::zero(), res) - } -} - -impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - (self.0.remove_spaces(arena), self.1.remove_spaces(arena)) - } -} - -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let mut items = Vec::with_capacity_in(self.items.len(), arena); - for item in self.items { - items.push(item.remove_spaces(arena)); - } - Collection::with_items(items.into_bump_slice()) - } -} - -impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let mut items = Vec::with_capacity_in(self.len(), arena); - for item in *self { - let res = item.remove_spaces(arena); - items.push(res); - } - items.into_bump_slice() - } -} - -impl<'a> RemoveSpaces<'a> for UnaryOp { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for BinOp { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - arena.alloc((*self).remove_spaces(arena)) - } -} - -impl<'a> RemoveSpaces<'a> for TypeDef<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - use TypeDef::*; - - match *self { - Alias { - header: TypeHeader { name, vars }, - ann, - } => Alias { - header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - ann: ann.remove_spaces(arena), - }, - Opaque { - header: TypeHeader { name, vars }, - typ, - derived, - } => Opaque { - header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - typ: typ.remove_spaces(arena), - derived: derived.remove_spaces(arena), - }, - Ability { - header: TypeHeader { name, vars }, - loc_implements: loc_has, - members, - } => Ability { - header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - loc_implements: loc_has.remove_spaces(arena), - members: members.remove_spaces(arena), - }, - } - } -} - -impl<'a> RemoveSpaces<'a> for ValueDef<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - use ValueDef::*; - - match *self { - Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)), - Body(a, b) => Body( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - AnnotatedBody { - ann_pattern, - ann_type, - comment: _, - body_pattern, - body_expr, - } => AnnotatedBody { - ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), - ann_type: arena.alloc(ann_type.remove_spaces(arena)), - comment: None, - body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), - body_expr: arena.alloc(body_expr.remove_spaces(arena)), - }, - Dbg { - condition, - preceding_comment: _, - } => Dbg { - condition: arena.alloc(condition.remove_spaces(arena)), - preceding_comment: Region::zero(), - }, - Expect { - condition, - preceding_comment: _, - } => Expect { - condition: arena.alloc(condition.remove_spaces(arena)), - preceding_comment: Region::zero(), - }, - ExpectFx { - condition, - preceding_comment: _, - } => ExpectFx { - condition: arena.alloc(condition.remove_spaces(arena)), - preceding_comment: Region::zero(), - }, - ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)), - IngestedFileImport(ingested_file_import) => { - IngestedFileImport(ingested_file_import.remove_spaces(arena)) - } - Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))), - } - } -} - -impl<'a> RemoveSpaces<'a> for ModuleImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ModuleImport { - before_name: &[], - name: self.name.remove_spaces(arena), - params: self.params.remove_spaces(arena), - alias: self.alias.remove_spaces(arena), - exposed: self.exposed.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ModuleImportParams { - before: &[], - params: self.params.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - IngestedFileImport { - before_path: &[], - path: self.path.remove_spaces(arena), - name: self.name.remove_spaces(arena), - annotation: self.annotation.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ImportedModuleName { - package: self.package.remove_spaces(arena), - name: self.name.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImportAlias<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for ImportAsKeyword { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for ImportExposingKeyword { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - IngestedFileAnnotation { - before_colon: &[], - annotation: self.annotation.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for Implements<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - Implements::Implements - } -} - -impl<'a> RemoveSpaces<'a> for AbilityMember<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - AbilityMember { - name: self.name.remove_spaces(arena), - typ: self.typ.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for WhenBranch<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - WhenBranch { - patterns: self.patterns.remove_spaces(arena), - value: self.value.remove_spaces(arena), - guard: self.guard.remove_spaces(arena), - } - } -} - -impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue( - a.remove_spaces(arena), - arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), - ), - AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue( - a.remove_spaces(arena), - arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), - ), - AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)), - AssignedField::Malformed(a) => AssignedField::Malformed(a), - AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena), - AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value( - a.remove_spaces(arena), - &[], - arena.alloc(c.remove_spaces(arena)), - ), - OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue( - a.remove_spaces(arena), - &[], - &[], - arena.alloc(c.remove_spaces(arena)), - ), - OldRecordBuilderField::LabelOnly(a) => { - OldRecordBuilderField::LabelOnly(a.remove_spaces(arena)) - } - OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a), - OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena), - OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t), - StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)), - StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)), - } - } -} - -impl<'a> RemoveSpaces<'a> for StrSegment<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - StrSegment::Plaintext(t) => StrSegment::Plaintext(t), - StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)), - StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c), - StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)), - StrSegment::DeprecatedInterpolated(t) => { - StrSegment::DeprecatedInterpolated(t.remove_spaces(arena)) - } - } - } -} - -impl<'a> RemoveSpaces<'a> for Expr<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Expr::Float(a) => Expr::Float(a), - Expr::Num(a) => Expr::Num(a), - Expr::NonBase10Int { - string, - base, - is_negative, - } => Expr::NonBase10Int { - string, - base, - is_negative, - }, - Expr::Str(a) => Expr::Str(a.remove_spaces(arena)), - Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b), - Expr::AccessorFunction(a) => Expr::AccessorFunction(a), - Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b), - Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))), - Expr::List(a) => Expr::List(a.remove_spaces(arena)), - Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { - update: arena.alloc(update.remove_spaces(arena)), - fields: fields.remove_spaces(arena), - }, - Expr::Record(a) => Expr::Record(a.remove_spaces(arena)), - Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)), - Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder { - mapper: arena.alloc(mapper.remove_spaces(arena)), - fields: fields.remove_spaces(arena), - }, - Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)), - Expr::Var { module_name, ident } => Expr::Var { module_name, ident }, - Expr::Underscore(a) => Expr::Underscore(a), - Expr::Tag(a) => Expr::Tag(a), - Expr::OpaqueRef(a) => Expr::OpaqueRef(a), - Expr::Closure(a, b) => Expr::Closure( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::Crash => Expr::Crash, - Expr::Defs(a, b) => { - let mut defs = a.clone(); - defs.space_before = vec![Default::default(); defs.len()]; - defs.space_after = vec![Default::default(); defs.len()]; - defs.regions = vec![Region::zero(); defs.len()]; - defs.spaces.clear(); - - for type_def in defs.type_defs.iter_mut() { - *type_def = type_def.remove_spaces(arena); - } - - for value_def in defs.value_defs.iter_mut() { - *value_def = value_def.remove_spaces(arena); - } - - Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena))) - } - Expr::Backpassing(a, b, c) => Expr::Backpassing( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - arena.alloc(c.remove_spaces(arena)), - ), - Expr::Expect(a, b) => Expr::Expect( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::Dbg(a, b) => Expr::Dbg( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::LowLevelDbg(_, _, _) => unreachable!( - "LowLevelDbg should only exist after desugaring, not during formatting" - ), - Expr::Apply(a, b, c) => Expr::Apply( - arena.alloc(a.remove_spaces(arena)), - b.remove_spaces(arena), - c, - ), - Expr::BinOps(a, b) => { - Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))) - } - Expr::UnaryOp(a, b) => { - Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) - } - Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))), - Expr::When(a, b) => { - Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) - } - Expr::ParensAround(a) => { - // The formatter can remove redundant parentheses, so also remove these when normalizing for comparison. - a.remove_spaces(arena) - } - Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), - Expr::MalformedClosure => Expr::MalformedClosure, - Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), - Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), - Expr::MultipleOldRecordBuilders(a) => Expr::MultipleOldRecordBuilders(a), - Expr::UnappliedOldRecordBuilder(a) => Expr::UnappliedOldRecordBuilder(a), - Expr::EmptyRecordBuilder(a) => Expr::EmptyRecordBuilder(a), - Expr::SingleFieldRecordBuilder(a) => Expr::SingleFieldRecordBuilder(a), - Expr::OptionalFieldInRecordBuilder(name, a) => { - Expr::OptionalFieldInRecordBuilder(name, a) - } - Expr::SpaceBefore(a, _) => a.remove_spaces(arena), - Expr::SpaceAfter(a, _) => a.remove_spaces(arena), - Expr::SingleQuote(a) => Expr::Num(a), - } - } -} - -fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent { - match ident { - BadIdent::Start(_) => BadIdent::Start(Position::zero()), - BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()), - BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()), - BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()), - BadIdent::UnderscoreAtStart { - position: _, - declaration_region, - } => BadIdent::UnderscoreAtStart { - position: Position::zero(), - declaration_region, - }, - BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()), - BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()), - BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()), - BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()), - BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()), - BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()), - BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()), - } -} - -impl<'a> RemoveSpaces<'a> for Pattern<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Pattern::Identifier { ident } => Pattern::Identifier { ident }, - Pattern::Tag(a) => Pattern::Tag(a), - Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), - Pattern::Apply(a, b) => Pattern::Apply( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)), - Pattern::RequiredField(a, b) => { - Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena))) - } - Pattern::OptionalField(a, b) => { - Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena))) - } - Pattern::As(pattern, pattern_as) => Pattern::As( - arena.alloc(pattern.remove_spaces(arena)), - pattern_as.remove_spaces(arena), - ), - Pattern::NumLiteral(a) => Pattern::NumLiteral(a), - Pattern::NonBase10Literal { - string, - base, - is_negative, - } => Pattern::NonBase10Literal { - string, - base, - is_negative, - }, - Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a), - Pattern::StrLiteral(a) => Pattern::StrLiteral(a), - Pattern::Underscore(a) => Pattern::Underscore(a), - Pattern::Malformed(a) => Pattern::Malformed(a), - Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)), - Pattern::QualifiedIdentifier { module_name, ident } => { - Pattern::QualifiedIdentifier { module_name, ident } - } - Pattern::SpaceBefore(a, _) => a.remove_spaces(arena), - Pattern::SpaceAfter(a, _) => a.remove_spaces(arena), - Pattern::SingleQuote(a) => Pattern::SingleQuote(a), - Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)), - Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)), - Pattern::ListRest(opt_pattern_as) => Pattern::ListRest( - opt_pattern_as - .map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))), - ), - } - } -} - -impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - TypeAnnotation::Function(a, b) => TypeAnnotation::Function( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)), - TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a), - TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As( - arena.alloc(a.remove_spaces(arena)), - &[], - TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - ), - TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple { - elems: fields.remove_spaces(arena), - ext: ext.remove_spaces(arena), - }, - TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record { - fields: fields.remove_spaces(arena), - ext: ext.remove_spaces(arena), - }, - TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion { - ext: ext.remove_spaces(arena), - tags: tags.remove_spaces(arena), - }, - TypeAnnotation::Inferred => TypeAnnotation::Inferred, - TypeAnnotation::Wildcard => TypeAnnotation::Wildcard, - TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where( - arena.alloc(annot.remove_spaces(arena)), - arena.alloc(has_clauses.remove_spaces(arena)), - ), - TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena), - TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena), - TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ImplementsClause { - var: self.var.remove_spaces(arena), - abilities: self.abilities.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for Tag<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Tag::Apply { name, args } => Tag::Apply { - name: name.remove_spaces(arena), - args: args.remove_spaces(arena), - }, - Tag::Malformed(a) => Tag::Malformed(a), - Tag::SpaceBefore(a, _) => a.remove_spaces(arena), - Tag::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - AbilityImpls::AbilityImpls(impls) => { - AbilityImpls::AbilityImpls(impls.remove_spaces(arena)) - } - AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => { - has.remove_spaces(arena) - } - } - } -} - -impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - ImplementsAbility::ImplementsAbility { ability, impls } => { - ImplementsAbility::ImplementsAbility { - ability: ability.remove_spaces(arena), - impls: impls.remove_spaces(arena), - } - } - ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => { - has.remove_spaces(arena) - } - } - } -} - -impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - ImplementsAbilities::Implements(derived) => { - ImplementsAbilities::Implements(derived.remove_spaces(arena)) - } - ImplementsAbilities::SpaceBefore(derived, _) - | ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for PatternAs<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - PatternAs { - spaces_before: &[], - identifier: self.identifier.remove_spaces(arena), - } - } -} diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 4eb61cef502..c50f453235e 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -4320,16 +4320,26 @@ mod test_reporting { { x, y } " ), - @r" - ── TOO MANY ARGS in /code/proj/Main.roc ──────────────────────────────────────── + @r###" + ── STATEMENT AFTER EXPRESSION in tmp/double_equals_in_def/Test.roc ───────────── + + I just finished parsing an expression with a series of definitions, - This value is not a function, but it was given 3 arguments: + and this line is indented as if it's intended to be part of that + expression: + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ x = 3 + 5│ y = 6│ x == 5 - ^ + 7│ Num.add 1 2 + ^ - Are there any missing commas? Or missing parentheses? - " + However, I already saw the final expression in that series of + definitions. + "### ); test_report!( @@ -5018,7 +5028,7 @@ mod test_reporting { I was partway through parsing an `import`, but I got stuck here: 4│ import svg.Path a - ^ + ^ I was expecting to see the `as` keyword, like: @@ -5417,14 +5427,25 @@ mod test_reporting { 2 -> 2 " ), - @r" - ── NOT END OF FILE in tmp/when_outdented_branch/Test.roc ─────────────────────── + @r###" + ── UNKNOWN OPERATOR in tmp/when_outdented_branch/Test.roc ────────────────────── - I expected to reach the end of the file, but got stuck here: + This looks like an operator, but it's not one I recognize! + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ when 4 is + 5│ 5 -> 2 6│ 2 -> 2 - ^ - " + ^^ + + Looks like you are trying to define a function. + + In roc, functions are always written as a lambda, like + + increment = \n -> n + 1 + "### ); test_report!( @@ -5436,12 +5457,13 @@ mod test_reporting { _ -> 2 " ), - @r" + @r###" ── UNEXPECTED ARROW in tmp/when_over_indented_underscore/Test.roc ────────────── I am parsing a `when` expression right now, but this arrow is confusing me: + 4│ when 4 is 5│ 5 -> 2 6│ _ -> 2 ^^ @@ -5461,7 +5483,7 @@ mod test_reporting { Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! - " + "### ); test_report!( @@ -5473,12 +5495,13 @@ mod test_reporting { 2 -> 2 " ), - @r" + @r###" ── UNEXPECTED ARROW in tmp/when_over_indented_int/Test.roc ───────────────────── I am parsing a `when` expression right now, but this arrow is confusing me: + 4│ when 4 is 5│ 5 -> Num.neg 6│ 2 -> 2 ^^ @@ -5498,7 +5521,7 @@ mod test_reporting { Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! - " + "### ); // TODO I think we can do better here @@ -6136,27 +6159,21 @@ All branches in an `if` must have the same type! main = 5 -> 3 " ), - |golden| pretty_assertions::assert_eq!( - golden, - &format!( - r#"── UNKNOWN OPERATOR in tmp/wild_case_arrow/Test.roc ──────────────────────────── - -This looks like an operator, but it's not one I recognize! - -1│ app "test" provides [main] to "./platform" -2│ -3│ main = -4│ main = 5 -> 3 - ^^ + @r###" + ── SYNTAX PROBLEM in tmp/wild_case_arrow/Test.roc ────────────────────────────── -Looks like you are trying to define a function.{} + I got stuck here: -In roc, functions are always written as a lambda, like{} + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ main = 5 -> 3 + ^ - increment = \n -> n + 1"#, - ' ', ' ' - ) - ) + Whatever I am running into is confusing me a lot! Normally I can give + fairly specific hints, but something is really tripping me up this + time. + "### ); #[test] @@ -10971,13 +10988,13 @@ In roc, functions are always written as a lambda, like{} 0 " ), - @r" + @r###" ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── This destructure assignment doesn't introduce any new variables: 4│ Pair _ _ = Pair 0 1 - ^^^^ + ^^^^^^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely @@ -11019,7 +11036,7 @@ In roc, functions are always written as a lambda, like{} assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! - " + "### ); test_report!( diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index de6014feba8..ddb664568a2 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -833,7 +833,7 @@ fn platform_parse_error() { match multiple_modules("platform_parse_error", modules) { Err(report) => { - assert!(report.contains("NOT END OF FILE")); + assert!(report.contains("STATEMENT AFTER EXPRESSION")); assert!(report.contains("blah 1 2 3 # causing a parse error on purpose")); } Ok(_) => unreachable!("we expect failure here"), diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index 72fc2c97810..43780ba9e15 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -3,7 +3,7 @@ use self::BinOp::*; use std::cmp::Ordering; use std::fmt; -const PRECEDENCES: [(BinOp, u8); 20] = [ +const PRECEDENCES: [(BinOp, u8); 16] = [ (Caret, 8), (Star, 7), (Slash, 7), @@ -20,14 +20,9 @@ const PRECEDENCES: [(BinOp, u8); 20] = [ (GreaterThanOrEq, 2), (And, 1), (Or, 0), - // These should never come up - (Assignment, 255), - (IsAliasType, 255), - (IsOpaqueType, 255), - (Backpassing, 255), ]; -const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [ +const ASSOCIATIVITIES: [(BinOp, Associativity); 16] = [ (Caret, RightAssociative), (Star, LeftAssociative), (Slash, LeftAssociative), @@ -44,14 +39,9 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [ (GreaterThanOrEq, NonAssociative), (And, RightAssociative), (Or, RightAssociative), - // These should never come up - (Assignment, LeftAssociative), - (IsAliasType, LeftAssociative), - (IsOpaqueType, LeftAssociative), - (Backpassing, LeftAssociative), ]; -const DISPLAY_STRINGS: [(BinOp, &str); 20] = [ +const DISPLAY_STRINGS: [(BinOp, &str); 16] = [ (Caret, "^"), (Star, "*"), (Slash, "/"), @@ -68,10 +58,6 @@ const DISPLAY_STRINGS: [(BinOp, &str); 20] = [ (GreaterThanOrEq, ">="), (And, "&&"), (Or, "||"), - (Assignment, "="), - (IsAliasType, ":"), - (IsOpaqueType, ":="), - (Backpassing, "<-"), ]; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -147,10 +133,6 @@ pub enum BinOp { GreaterThanOrEq, And, Or, - Assignment, - IsAliasType, - IsOpaqueType, - Backpassing, // lowest precedence } @@ -161,7 +143,6 @@ impl BinOp { Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1, DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or | Pizza => 2, - Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(), } } } @@ -195,25 +176,13 @@ pub enum Associativity { impl BinOp { pub fn associativity(self) -> Associativity { - // The compiler should never pass any of these to this function! - debug_assert_ne!(self, Assignment); - debug_assert_ne!(self, IsAliasType); - debug_assert_ne!(self, IsOpaqueType); - debug_assert_ne!(self, Backpassing); - - const ASSOCIATIVITY_TABLE: [Associativity; 20] = generate_associativity_table(); + const ASSOCIATIVITY_TABLE: [Associativity; 16] = generate_associativity_table(); ASSOCIATIVITY_TABLE[self as usize] } fn precedence(self) -> u8 { - // The compiler should never pass any of these to this function! - debug_assert_ne!(self, Assignment); - debug_assert_ne!(self, IsAliasType); - debug_assert_ne!(self, IsOpaqueType); - debug_assert_ne!(self, Backpassing); - - const PRECEDENCE_TABLE: [u8; 20] = generate_precedence_table(); + const PRECEDENCE_TABLE: [u8; 16] = generate_precedence_table(); PRECEDENCE_TABLE[self as usize] } @@ -233,19 +202,14 @@ impl Ord for BinOp { impl std::fmt::Display for BinOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - debug_assert_ne!(*self, Assignment); - debug_assert_ne!(*self, IsAliasType); - debug_assert_ne!(*self, IsOpaqueType); - debug_assert_ne!(*self, Backpassing); - - const DISPLAY_TABLE: [&str; 20] = generate_display_table(); + const DISPLAY_TABLE: [&str; 16] = generate_display_table(); write!(f, "{}", DISPLAY_TABLE[*self as usize]) } } -const fn generate_precedence_table() -> [u8; 20] { - let mut table = [0u8; 20]; +const fn generate_precedence_table() -> [u8; 16] { + let mut table = [0u8; 16]; let mut i = 0; while i < PRECEDENCES.len() { @@ -256,8 +220,8 @@ const fn generate_precedence_table() -> [u8; 20] { table } -const fn generate_associativity_table() -> [Associativity; 20] { - let mut table = [NonAssociative; 20]; +const fn generate_associativity_table() -> [Associativity; 16] { + let mut table = [NonAssociative; 16]; let mut i = 0; while i < ASSOCIATIVITIES.len() { @@ -268,8 +232,8 @@ const fn generate_associativity_table() -> [Associativity; 20] { table } -const fn generate_display_table() -> [&'static str; 20] { - let mut table = [""; 20]; +const fn generate_display_table() -> [&'static str; 16] { + let mut table = [""; 16]; let mut i = 0; while i < DISPLAY_STRINGS.len() { diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 81b3e408bfe..4ff90c5a528 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -21,6 +21,12 @@ pub struct Spaces<'a, T> { pub after: &'a [CommentOrNewline<'a>], } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SpacesBefore<'a, T> { + pub before: &'a [CommentOrNewline<'a>], + pub item: T, +} + #[derive(Copy, Clone, PartialEq)] pub enum Spaced<'a, T> { Item(T), @@ -1204,6 +1210,21 @@ impl<'a> Defs<'a> { }) } + pub fn loc_defs<'b>( + &'b self, + ) -> impl Iterator>, Loc>>> + 'b { + self.tags + .iter() + .enumerate() + .map(|(i, tag)| match tag.split() { + Ok(type_index) => Ok(Loc::at(self.regions[i], self.type_defs[type_index.index()])), + Err(value_index) => Err(Loc::at( + self.regions[i], + self.value_defs[value_index.index()], + )), + }) + } + pub fn list_value_defs(&self) -> impl Iterator)> { self.tags .iter() @@ -2072,6 +2093,28 @@ pub trait Spaceable<'a> { fn before(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self; fn after(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self; + fn maybe_before(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self + where + Self: Sized + 'a, + { + if spaces.is_empty() { + self + } else { + arena.alloc(self).before(spaces) + } + } + + fn maybe_after(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self + where + Self: Sized + 'a, + { + if spaces.is_empty() { + self + } else { + arena.alloc(self).after(spaces) + } + } + fn with_spaces_before(&'a self, spaces: &'a [CommentOrNewline<'a>], region: Region) -> Loc where Self: Sized, diff --git a/crates/compiler/parse/src/blankspace.rs b/crates/compiler/parse/src/blankspace.rs index 9b57207619e..2d3f36eda68 100644 --- a/crates/compiler/parse/src/blankspace.rs +++ b/crates/compiler/parse/src/blankspace.rs @@ -333,7 +333,7 @@ where let start = state.pos(); match spaces().parse(arena, state, min_indent) { Ok((progress, spaces, state)) => { - if progress == NoProgress || state.column() >= min_indent { + if spaces.is_empty() || state.column() >= min_indent { Ok((progress, spaces, state)) } else { Err((progress, indent_problem(start))) @@ -344,6 +344,60 @@ where } } +pub fn require_newline_or_eof<'a, E>(newline_problem: fn(Position) -> E) -> impl Parser<'a, (), E> +where + E: 'a + SpaceProblem, +{ + move |arena: &'a Bump, state: State<'a>, min_indent| { + // TODO: we can do this more efficiently by stopping as soon as we see a '#' or a newline + let (_, res, _) = space0_e(newline_problem).parse(arena, state.clone(), min_indent)?; + + if !res.is_empty() || state.has_reached_end() { + Ok((NoProgress, (), state)) + } else { + Err((NoProgress, newline_problem(state.pos()))) + } + } +} + +pub fn loc_space0_e<'a, E>( + indent_problem: fn(Position) -> E, +) -> impl Parser<'a, Loc<&'a [CommentOrNewline<'a>]>, E> +where + E: 'a + SpaceProblem, +{ + move |arena, state: State<'a>, min_indent: u32| { + let mut newlines = Vec::new_in(arena); + let start = state.pos(); + let mut comment_start = None; + let mut comment_end = None; + + let res = consume_spaces(state, |start, space, end| { + newlines.push(space); + if !matches!(space, CommentOrNewline::Newline) { + if comment_start.is_none() { + comment_start = Some(start); + } + comment_end = Some(end); + } + }); + + match res { + Ok((progress, state)) => { + if newlines.is_empty() || state.column() >= min_indent { + let start = comment_start.unwrap_or(state.pos()); + let end = comment_end.unwrap_or(state.pos()); + let region = Region::new(start, end); + Ok((progress, Loc::at(region, newlines.into_bump_slice()), state)) + } else { + Err((progress, indent_problem(start))) + } + } + Err((progress, err)) => Err((progress, err)), + } + } +} + fn begins_with_crlf(bytes: &[u8]) -> bool { bytes.len() >= 2 && bytes[0] == b'\r' && bytes[1] == b'\n' } @@ -387,7 +441,7 @@ where F: FnMut(Position, CommentOrNewline<'a>, Position), { let mut progress = NoProgress; - let mut found_newline = false; + let mut found_newline = state.is_at_start_of_file(); loop { let whitespace = fast_eat_whitespace(state.bytes()); if whitespace > 0 { diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 072bae31366..eaa5a1eaccd 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,12 +1,12 @@ use crate::ast::{ - is_expr_suffixed, is_top_level_suffixed, AssignedField, Collection, CommentOrNewline, Defs, - Expr, ExtractSpaces, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, - ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, - ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, + is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, + Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, + ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, + ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, ValueDef, }; use crate::blankspace::{ - space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e, + loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before, }; use crate::ident::{ @@ -15,13 +15,13 @@ use crate::ident::{ use crate::module::module_name_help; use crate::parser::{ self, and, backtrackable, between, byte, byte_indent, collection_inner, - collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, - line_min_indent, loc, map, map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, - set_min_indent, skip_first, skip_second, specialize_err, specialize_err_ref, then, two_bytes, - zero_or_more, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, - EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser, + collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map, + map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first, + skip_second, specialize_err, specialize_err_ref, then, two_bytes, zero_or_more, EClosure, + EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, EPattern, ERecord, + EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem, }; -use crate::pattern::{closure_param, loc_implements_parser}; +use crate::pattern::closure_param; use crate::state::State; use crate::string_literal::{self, StrLikeLiteral}; use crate::{header, keyword}; @@ -51,7 +51,7 @@ pub fn test_parse_expr<'a>( state: State<'a>, ) -> Result>, EExpr<'a>> { let parser = skip_second( - space0_before_optional_after(loc_expr(true), EExpr::IndentStart, EExpr::IndentEnd), + space0_before_optional_after(loc_expr_block(true), EExpr::IndentStart, EExpr::IndentEnd), expr_end(), ); @@ -88,7 +88,11 @@ fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc>, EInParens<'a> then( loc(collection_trailing_sep_e( byte(b'(', EInParens::Open), - specialize_err_ref(EInParens::Expr, loc_expr(false)), + specialize_err_ref( + EInParens::Expr, + // space0_before_e( + loc_expr_block(false), + ), byte(b',', EInParens::End), byte(b')', EInParens::End), Expr::SpaceBefore, @@ -177,7 +181,7 @@ fn loc_term_or_underscore_or_conditional<'a>( one_of!( loc_expr_in_parens_etc_help(), loc(specialize_err(EExpr::If, if_expr_help(options))), - loc(specialize_err(EExpr::When, when::expr_help(options))), + loc(specialize_err(EExpr::When, when::when_expr_help(options))), loc(specialize_err(EExpr::Str, string_like_literal_help())), loc(specialize_err( EExpr::Number, @@ -190,6 +194,7 @@ fn loc_term_or_underscore_or_conditional<'a>( loc(specialize_err(EExpr::List, list_literal_help())), ident_seq(), ) + .trace("term_or_underscore_or_conditional") } /// In some contexts we want to parse the `_` as an expression, so it can then be turned into a @@ -210,6 +215,7 @@ fn loc_term_or_underscore<'a>( loc(specialize_err(EExpr::List, list_literal_help())), ident_seq(), ) + .trace("term_or_underscore") } fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, EExpr<'a>> { @@ -336,10 +342,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> { fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, EExpr<'a>> { one_of![ loc(specialize_err(EExpr::If, if_expr_help(options))), - loc(specialize_err(EExpr::When, when::expr_help(options))), - loc(specialize_err(EExpr::Expect, expect_help(options))), - loc(specialize_err(EExpr::Dbg, dbg_help(options))), - loc(import_help(options)), + loc(specialize_err(EExpr::When, when::when_expr_help(options))), loc(specialize_err(EExpr::Closure, closure_help(options))), loc(expr_operator_chain(options)), fail_expr_start_e() @@ -348,59 +351,328 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, E } fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { - line_min_indent(move |arena, state: State<'a>, min_indent: u32| { - let end = state.pos(); + (move |arena, state: State<'a>, min_indent: u32| { + parse_expr_operator_chain(arena, state, min_indent, options) + }) + .trace("expr_operator_chain") +} + +fn parse_expr_operator_chain<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + options: ExprParseOptions, +) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> { + let line_indent = state.line_indent(); - let (_, expr, state) = - loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?; + let (_, expr, state) = + loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?; - let initial_state = state.clone(); + let mut initial_state = state.clone(); - let new_min_indent = if is_expr_suffixed(&expr.value) { - min_indent + 1 - } else { - min_indent + let (spaces_before_op, state) = + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => return Ok((MadeProgress, expr.value, state)), + Ok((_, spaces_before_op, state)) => (spaces_before_op, state), }; - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => Ok((MadeProgress, expr.value, state)), - Ok((_, spaces_before_op, state)) => { - let expr_state = ExprState { - operators: Vec::new_in(arena), - arguments: Vec::new_in(arena), - expr, - spaces_after: spaces_before_op, - end: initial_state.pos(), - }; + let mut expr_state = ExprState { + operators: Vec::new_in(arena), + arguments: Vec::new_in(arena), + expr, + spaces_after: spaces_before_op, + end: initial_state.pos(), + }; + + let mut state = state; + + let call_min_indent = line_indent + 1; - match parse_expr_end( - new_min_indent, + loop { + let parser = skip_first( + crate::blankspace::check_indent(EExpr::IndentEnd), + loc_term_or_underscore(options), + ); + match parser.parse(arena, state.clone(), call_min_indent) { + Err((MadeProgress, f)) => return Err((MadeProgress, f)), + Err((NoProgress, _)) => { + let before_op = state.clone(); + // try an operator + return parse_expr_after_apply( + arena, + state, + min_indent, + call_min_indent, options, + true, expr_state, + before_op, + initial_state, + ); + } + Ok((_, arg, new_state)) => { + state = new_state; + initial_state = state.clone(); + + if parse_after_expr_arg_and_check_final( + arena, + &mut state, + min_indent, + &mut expr_state, + arg, + ) { + let expr = parse_expr_final(expr_state, arena); + return Ok((MadeProgress, expr, state)); + } + + continue; + } + } + } +} + +#[allow(clippy::too_many_arguments)] +fn parse_expr_after_apply<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + check_for_defs: bool, + mut expr_state: ExprState<'a>, + before_op: State<'a>, + initial_state: State<'a>, +) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> { + match loc(bin_op(check_for_defs)).parse(arena, state.clone(), min_indent) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), + Ok((_, loc_op, state)) => { + expr_state.consume_spaces(arena); + let initial_state = before_op; + parse_expr_operator( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + loc_op, + initial_state, + ) + } + Err((NoProgress, _)) => { + let expr = parse_expr_final(expr_state, arena); + // roll back space parsing + Ok((MadeProgress, expr, initial_state)) + } + } +} + +fn expr_to_stmt(expr: Loc>) -> Loc> { + Loc::at(expr.region, Stmt::Expr(expr.value)) +} + +pub fn parse_repl_defs_and_optional_expr<'a>( + arena: &'a Bump, + state: State<'a>, +) -> ParseResult<'a, (Defs<'a>, Option>>), EExpr<'a>> { + let initial_state = state.clone(); + let (spaces_before, state) = match loc(space0_e(EExpr::IndentEnd)).parse(arena, state, 0) { + Err((NoProgress, _)) => return Ok((NoProgress, (Defs::default(), None), initial_state)), + Err((MadeProgress, e)) => return Err((MadeProgress, e)), + Ok((_, sp, state)) => (sp, state), + }; + + let (_, stmts, state) = parse_stmt_seq( + arena, + state, + |e, _| e.clone(), + ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }, + 0, + spaces_before, + EExpr::IndentEnd, + )?; + + let state = match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), 0) { + Err((NoProgress, _)) => state, + Err((MadeProgress, e)) => return Err((MadeProgress, e)), + Ok((_, _, state)) => state, + }; + + if !state.has_reached_end() { + return Err((MadeProgress, EExpr::End(state.pos()))); + } + + let (defs, last_expr) = + stmts_to_defs(&stmts, Defs::default(), false, arena).map_err(|e| (MadeProgress, e))?; + + Ok((MadeProgress, (defs, last_expr), state)) +} + +fn stmt_start<'a>( + options: ExprParseOptions, + preceding_comment: Region, +) -> impl Parser<'a, Loc>, EExpr<'a>> { + one_of![ + map( + loc(specialize_err(EExpr::If, if_expr_help(options))), + expr_to_stmt + ), + map( + loc(specialize_err(EExpr::When, when::when_expr_help(options))), + expr_to_stmt + ), + loc(specialize_err( + EExpr::Expect, + expect_help(options, preceding_comment) + )), + loc(specialize_err( + EExpr::Dbg, + dbg_help(options, preceding_comment) + )), + loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))), + map( + loc(specialize_err(EExpr::Closure, closure_help(options))), + expr_to_stmt + ), + loc(stmt_operator_chain(options)), + fail_expr_start_e() + ] + .trace("stmt_start") +} + +fn stmt_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Stmt<'a>, EExpr<'a>> { + (move |arena, state: State<'a>, min_indent: u32| { + parse_stmt_operator_chain(arena, state, min_indent, options) + }) + .trace("stmt_operator_chain") +} + +fn parse_stmt_operator_chain<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + options: ExprParseOptions, +) -> Result<(Progress, Stmt<'a>, State<'a>), (Progress, EExpr<'a>)> { + let line_indent = state.line_indent(); + + let (_, expr, state) = + loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?; + + let mut initial_state = state.clone(); + let end = state.pos(); + + let (spaces_before_op, state) = + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => return Ok((MadeProgress, Stmt::Expr(expr.value), state)), + Ok((_, spaces_before_op, state)) => (spaces_before_op, state), + }; + + let mut expr_state = ExprState { + operators: Vec::new_in(arena), + arguments: Vec::new_in(arena), + expr, + spaces_after: spaces_before_op, + end, + }; + + let mut state = state; + + let call_min_indent = line_indent + 1; + + loop { + let parser = skip_first( + crate::blankspace::check_indent(EExpr::IndentEnd), + loc_term_or_underscore(options), + ); + match parser.parse(arena, state.clone(), call_min_indent) { + Err((MadeProgress, f)) => return Err((MadeProgress, f)), + Ok(( + _, + implements @ Loc { + value: + Expr::Var { + module_name: "", + ident: crate::keyword::IMPLEMENTS, + .. + }, + .. + }, + state, + )) if matches!(expr_state.expr.value, Expr::Tag(..)) => { + return parse_ability_def(expr_state, state, arena, implements, call_min_indent) + .map(|(td, s)| (MadeProgress, Stmt::TypeDef(td), s)); + } + Err((NoProgress, _)) => { + // try an operator + return parse_stmt_after_apply( arena, state, + min_indent, + call_min_indent, + expr_state, + options, initial_state, + ); + } + Ok((_, arg, new_state)) => { + state = new_state; + initial_state = state.clone(); + + if parse_after_expr_arg_and_check_final( + arena, + &mut state, + min_indent, + &mut expr_state, + arg, ) { - Err(err) => Err(err), - Ok((progress, expr, new_state)) => { - // We need to check if we have just parsed a suffixed statement, - // if so, this is a defs node. - if is_top_level_suffixed(&expr) { - let def_region = Region::new(end, new_state.pos()); - let value_def = ValueDef::Stmt(arena.alloc(Loc::at(def_region, expr))); - - let mut defs = Defs::default(); - defs.push_value_def(value_def, def_region, &[], &[]); - - return parse_defs_expr(options, min_indent, defs, arena, new_state); - } else { - Ok((progress, expr, new_state)) - } - } + let expr = parse_expr_final(expr_state, arena); + return Ok((MadeProgress, Stmt::Expr(expr), state)); } + + continue; } } - }) + } +} + +fn parse_after_expr_arg_and_check_final<'a>( + arena: &'a Bump, + state: &mut State<'a>, + min_indent: u32, + expr_state: &mut ExprState<'a>, + mut arg: Loc>, +) -> bool { + let new_end = state.pos(); + + if !expr_state.spaces_after.is_empty() { + arg = arena + .alloc(arg.value) + .with_spaces_before(expr_state.spaces_after, arg.region); + + expr_state.spaces_after = &[]; + } + let new_spaces = match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = &[]; + + return true; + } + Ok((_, new_spaces, new_state)) => { + *state = new_state; + new_spaces + } + }; + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = new_spaces; + + false } #[derive(Debug)] @@ -437,7 +709,7 @@ impl<'a> ExprState<'a> { fn validate_assignment_or_backpassing( mut self, arena: &'a Bump, - loc_op: Loc, + loc_op: Loc, argument_error: F, ) -> Result>, EExpr<'a>> where @@ -446,8 +718,8 @@ impl<'a> ExprState<'a> { if !self.operators.is_empty() { // this `=` or `<-` likely occurred inline; treat it as an invalid operator let opchar = match loc_op.value { - BinOp::Assignment => "=", - BinOp::Backpassing => "<-", + OperatorOrDef::Assignment => "=", + OperatorOrDef::Backpassing => "<-", _ => unreachable!(), }; @@ -471,24 +743,15 @@ impl<'a> ExprState<'a> { fn validate_is_type_def( mut self, arena: &'a Bump, - loc_op: Loc, - kind: AliasOrOpaque, + kind: Loc, ) -> Result<(Loc>, Vec<'a, &'a Loc>>), EExpr<'a>> { - debug_assert_eq!( - loc_op.value, - match kind { - AliasOrOpaque::Alias => BinOp::IsAliasType, - AliasOrOpaque::Opaque => BinOp::IsOpaqueType, - } - ); - if !self.operators.is_empty() { // this `:`/`:=` likely occurred inline; treat it as an invalid operator - let op = match kind { + let op = match kind.value { AliasOrOpaque::Alias => ":", AliasOrOpaque::Opaque => ":=", }; - let fail = EExpr::BadOperator(op, loc_op.region.start()); + let fail = EExpr::BadOperator(op, kind.region.start()); Err(fail) } else { @@ -607,440 +870,70 @@ fn numeric_negate_expression<'a, T>( } } -pub fn parse_single_def<'a>( - options: ExprParseOptions, - min_indent: u32, - arena: &'a Bump, - state: State<'a>, -) -> ParseResult<'a, Option>, EExpr<'a>> { - let initial = state.clone(); - - let mut spaces_before_current = &[] as &[_]; - let spaces_before_current_start = state.pos(); - - let state = match space0_e(EExpr::IndentStart).parse(arena, state, min_indent) { - Err((MadeProgress, bad_input @ EExpr::Space(_, _))) => { - return Err((MadeProgress, bad_input)); - } - Err((MadeProgress, _)) => { - return Err((MadeProgress, EExpr::DefMissingFinalExpr(initial.pos()))); - } - Ok((_, spaces, state)) => { - spaces_before_current = spaces; - state - } - Err((NoProgress, _)) => initial.clone(), - }; - - let start = state.pos(); - - let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect); - let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect); - let parse_expect = either(parse_expect_fx, parse_expect_vanilla); +fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> { + map( + record!(ModuleImport { + before_name: space0_e(EImport::IndentStart), + name: loc(imported_module_name()), + params: optional(specialize_err(EImport::Params, import_params())), + alias: optional(import_as()), + exposed: optional(import_exposing()) + }), + ValueDef::ModuleImport, + ) +} - match space0_after_e(crate::pattern::loc_pattern_help(), EPattern::IndentEnd).parse( - arena, - state.clone(), - min_indent, - ) { - Err((NoProgress, _)) => { - let pos_before_import = state.pos(); - match import().parse(arena, state.clone(), min_indent) { - Err((NoProgress, _)) => { - match parse_expect.parse(arena, state.clone(), min_indent) { - Err((_, _)) => { - // a hacky way to get expression-based error messages. TODO fix this - Ok((NoProgress, None, initial)) - } - Ok((_, expect_flavor, state)) => parse_statement_inside_def( - arena, - state, - min_indent, - options, - start, - spaces_before_current_start, - spaces_before_current, - |preceding_comment, loc_def_expr| match expect_flavor { - Either::Second(_) => ValueDef::Expect { - condition: arena.alloc(loc_def_expr), - preceding_comment, - }, - Either::First(_) => ValueDef::ExpectFx { - condition: arena.alloc(loc_def_expr), - preceding_comment, - }, - }, - ), +fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<'a>> { + then( + and( + backtrackable(space0_e(EImportParams::Indent)), + specialize_err(EImportParams::Record, record_help()), + ), + |arena, state, _, (before, record): (_, RecordHelp<'a>)| { + if let Some(prefix) = record.prefix { + match prefix { + (update, RecordHelpPrefix::Update) => { + return Err(( + MadeProgress, + EImportParams::RecordUpdateFound(update.region), + )) } - } - Err((MadeProgress, err)) => { - Err((MadeProgress, EExpr::Import(err, pos_before_import))) - } - Ok((_, (loc_import, spaces_after), state)) => Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(loc_import.value), - region: loc_import.region, - spaces_before: spaces_before_current, - spaces_after, - }), - state, - )), - } - } - Err((MadeProgress, _)) => { - // Try to parse as a Statement - match parse_statement_inside_def( - arena, - initial.clone(), - min_indent, - options, - start, - spaces_before_current_start, - // TODO including spaces_before_current here doubles things up - &[], - |_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) }, - ) { - Ok((_, Some(single_def), state)) => match single_def.type_or_value { - Either::Second(ValueDef::Stmt(loc_expr)) - if is_expr_suffixed(&loc_expr.value) => - { - Ok((MadeProgress, Some(single_def), state)) + (mapper, RecordHelpPrefix::Mapper) => { + return Err(( + MadeProgress, + EImportParams::RecordBuilderFound(mapper.region), + )) } - _ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this - }, - _ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this + } } - } - Ok((_, loc_pattern, state)) => { - // First let's check whether this is an ability definition. - let opt_tag_and_args: Option<(&str, Region, &[Loc])> = match loc_pattern.value - { - Pattern::Apply( - Loc { - value: Pattern::Tag(name), - region, - }, - args, - ) => Some((name, *region, args)), - Pattern::Tag(name) => Some((name, loc_pattern.region, &[])), - _ => None, - }; - - if let Some((name, name_region, args)) = opt_tag_and_args { - if let Ok((_, loc_implements, state)) = - loc_implements_parser().parse(arena, state.clone(), min_indent) - { - let (_, (type_def, def_region), state) = finish_parsing_ability_def_help( - min_indent, - Loc::at(name_region, name), - args, - loc_implements, - arena, - state, - )?; - return Ok(( + let params = record.fields.map_items_result(arena, |loc_field| { + match loc_field.value.to_assigned_field(arena) { + Ok(field) => Ok(Loc::at(loc_field.region, field)), + Err(FoundApplyValue) => Err(( MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region: def_region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); + EImportParams::RecordApplyFound(loc_field.region), + )), } - } + })?; - // This may be a def or alias. - let operator_result = operator().parse(arena, state.clone(), min_indent); + let import_params = ModuleImportParams { before, params }; - if let Ok((_, BinOp::Assignment, operator_result_state)) = operator_result { - return parse_single_def_assignment( - options, - // to support statements we have to increase the indent here so that we can parse a child def - // within a def and still continue to parse the final expression for this def - // e.g. - // main = - // Stdout.line! "Bar" - // a=Stdout.line! "Foo" - // Task.ok {} - &operator_result_state.line_indent() + 1, - arena, - operator_result_state, - loc_pattern, - spaces_before_current, - ); - }; + Ok((MadeProgress, import_params, state)) + }, + ) +} - if let Ok((_, BinOp::IsAliasType, state)) = operator_result { - // the increment_min_indent here is probably _wrong_, since alias_signature_with_space_before does - // that internally. - // TODO: re-evaluate this - let parser = increment_min_indent(alias_signature_with_space_before()); - let (_, ann_type, state) = parser.parse(arena, state, min_indent)?; - let region = Region::span_across(&loc_pattern.region, &ann_type.region); - - match &loc_pattern.value.extract_spaces().item { - Pattern::Apply( - Loc { - value: Pattern::Tag(name), - .. - }, - alias_arguments, - ) => { - let name = Loc::at(loc_pattern.region, *name); - let header = TypeHeader { - name, - vars: alias_arguments, - }; - - let type_def = TypeDef::Alias { - header, - ann: ann_type, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - Pattern::Tag(name) => { - let name = Loc::at(loc_pattern.region, *name); - let pattern_arguments: &'a [Loc>] = &[]; - let header = TypeHeader { - name, - vars: pattern_arguments, - }; - - let type_def = TypeDef::Alias { - header, - ann: ann_type, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - _ => { - let value_def = ValueDef::Annotation(loc_pattern, ann_type); - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - } - }; - - if let Ok((_, BinOp::IsOpaqueType, state)) = operator_result { - let (_, (signature, derived), state) = - opaque_signature_with_space_before().parse(arena, state, min_indent + 1)?; - let region = Region::span_across(&loc_pattern.region, &signature.region); - - match &loc_pattern.value.extract_spaces().item { - Pattern::Apply( - Loc { - value: Pattern::Tag(name), - .. - }, - alias_arguments, - ) => { - let name = Loc::at(loc_pattern.region, *name); - let header = TypeHeader { - name, - vars: alias_arguments, - }; - - let type_def = TypeDef::Opaque { - header, - typ: signature, - derived, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - Pattern::Tag(name) => { - let name = Loc::at(loc_pattern.region, *name); - let pattern_arguments: &'a [Loc>] = &[]; - let header = TypeHeader { - name, - vars: pattern_arguments, - }; - - let type_def = TypeDef::Opaque { - header, - typ: signature, - derived, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - _ => { - let value_def = ValueDef::Annotation(loc_pattern, signature); - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - } - }; - - // Otherwise try to re-parse as a Statement - match parse_statement_inside_def( - arena, - initial.clone(), - min_indent, - options, - start, - spaces_before_current_start, - // TODO figure out why including spaces_before_current here doubles things up - &[], - |_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) }, - ) { - Ok((_, Some(single_def), state)) => match single_def.type_or_value { - Either::Second(ValueDef::Stmt(loc_expr)) - if is_expr_suffixed(&loc_expr.value) => - { - Ok((MadeProgress, Some(single_def), state)) - } - _ => Ok((NoProgress, None, initial)), - }, - _ => Ok((NoProgress, None, initial)), - } - } - } -} - -fn import<'a>() -> impl Parser<'a, (Loc>, &'a [CommentOrNewline<'a>]), EImport<'a>> { - then( - and( - loc(skip_first( - parser::keyword(keyword::IMPORT, EImport::Import), - increment_min_indent(one_of!(import_body(), import_ingested_file_body())), - )), - space0_e(EImport::EndNewline), - ), - |_arena, state, progress, (import, spaces_after)| { - if !spaces_after.is_empty() || state.has_reached_end() { - Ok((progress, (import, spaces_after), state)) - } else { - // We require EOF, comment, or newline after import - Err((progress, EImport::EndNewline(state.pos()))) - } - }, - ) -} - -fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> { - map( - record!(ModuleImport { - before_name: space0_e(EImport::IndentStart), - name: loc(imported_module_name()), - params: optional(specialize_err(EImport::Params, import_params())), - alias: optional(import_as()), - exposed: optional(import_exposing()) - }), - ValueDef::ModuleImport, - ) -} - -fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<'a>> { - then( - and( - backtrackable(space0_e(EImportParams::Indent)), - specialize_err(EImportParams::Record, record_help()), - ), - |arena, state, _, (before, record): (_, RecordHelp<'a>)| { - if let Some(prefix) = record.prefix { - match prefix { - (update, RecordHelpPrefix::Update) => { - return Err(( - MadeProgress, - EImportParams::RecordUpdateFound(update.region), - )) - } - (mapper, RecordHelpPrefix::Mapper) => { - return Err(( - MadeProgress, - EImportParams::RecordBuilderFound(mapper.region), - )) - } - } - } - - let params = record.fields.map_items_result(arena, |loc_field| { - match loc_field.value.to_assigned_field(arena) { - Ok(field) => Ok(Loc::at(loc_field.region, field)), - Err(FoundApplyValue) => Err(( - MadeProgress, - EImportParams::RecordApplyFound(loc_field.region), - )), - } - })?; - - let import_params = ModuleImportParams { before, params }; - - Ok((MadeProgress, import_params, state)) - }, - ) -} - -#[inline(always)] -fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport<'a>> { - record!(ImportedModuleName { - package: optional(skip_second( - specialize_err(|_, pos| EImport::PackageShorthand(pos), lowercase_ident()), - byte(b'.', EImport::PackageShorthandDot) - )), - name: module_name_help(EImport::ModuleName) - }) -} +#[inline(always)] +fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport<'a>> { + record!(ImportedModuleName { + package: optional(skip_second( + specialize_err(|_, pos| EImport::PackageShorthand(pos), lowercase_ident()), + byte(b'.', EImport::PackageShorthandDot) + )), + name: module_name_help(EImport::ModuleName) + }) +} #[inline(always)] fn import_as<'a>( @@ -1147,353 +1040,11 @@ fn import_ingested_file_annotation<'a>() -> impl Parser<'a, IngestedFileAnnotati }) } -pub fn parse_single_def_assignment<'a>( - options: ExprParseOptions, - min_indent: u32, - arena: &'a Bump, - initial_state: State<'a>, - def_loc_pattern: Loc>, - spaces_before_current: &'a [CommentOrNewline<'a>], -) -> ParseResult<'a, Option>, EExpr<'a>> { - // Try and parse the expression - let parse_def_expr = - space0_before_e(increment_min_indent(expr_start(options)), EExpr::IndentEnd); - let (progress_after_first, first_loc_expr, state_after_first_expression) = - parse_def_expr.parse(arena, initial_state, min_indent)?; - - let region = Region::span_across(&def_loc_pattern.region, &first_loc_expr.region); - - // If the expression is actually a suffixed statement, then we need to continue - // to parse the rest of the expression - if is_top_level_suffixed(&first_loc_expr.value) { - let mut defs = Defs::default(); - // Take the suffixed value and make it a e.g. Body(`{}=`, Apply(Var(...))) - // we will keep the pattern `def_loc_pattern` for the new Defs - defs.push_value_def( - ValueDef::Stmt(arena.alloc(first_loc_expr)), - region, - spaces_before_current, - &[], - ); - - // Try to parse the rest of the expression as multiple defs, which may contain sub-assignments - match parse_defs_expr( - options, - min_indent, - defs, - arena, - state_after_first_expression, - ) { - Ok((progress_after_rest_of_def, expr, state_after_rest_of_def)) => { - let final_loc_expr = arena.alloc(Loc::at(region, expr)); - - let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), final_loc_expr); - - Ok(( - progress_after_rest_of_def, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state_after_rest_of_def, - )) - } - Err((progress, err)) => Err((progress, err)), - } - } else { - let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr)); - - Ok(( - progress_after_first, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state_after_first_expression, - )) - } -} - -/// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg` -#[allow(clippy::too_many_arguments)] -fn parse_statement_inside_def<'a>( - arena: &'a Bump, - state: State<'a>, - min_indent: u32, - options: ExprParseOptions, - start: Position, - spaces_before_current_start: Position, - spaces_before_current: &'a [CommentOrNewline<'a>], - get_value_def: impl Fn(Region, Loc>) -> ValueDef<'a>, -) -> Result<(Progress, Option>, State<'a>), (Progress, EExpr<'a>)> { - let parse_def_expr = - space0_before_e(increment_min_indent(expr_start(options)), EExpr::IndentEnd); - let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state, min_indent)?; - let end = loc_def_expr.region.end(); - let region = Region::new(start, end); - - // drop newlines before the preceding comment - let spaces_before_start = spaces_before_current_start.offset as usize; - let spaces_before_end = start.offset as usize; - let mut spaces_before_current_start = spaces_before_current_start; - - for byte in &state.original_bytes()[spaces_before_start..spaces_before_end] { - match byte { - b' ' | b'\n' => { - spaces_before_current_start.offset += 1; - } - _ => break, - } - } - - let preceding_comment = Region::new(spaces_before_current_start, start); - - let value_def = get_value_def(preceding_comment, loc_def_expr); - - Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )) -} - -// This is a macro only because trying to make it be a function caused lifetime issues. -#[macro_export] -macro_rules! join_ann_to_body { - ($arena:expr, $loc_pattern:expr, $loc_def_expr:expr, $ann_pattern:expr, $ann_type:expr, $spaces_before_current:expr, $region:expr) => {{ - // join this body with the preceding annotation - - let value_def = ValueDef::AnnotatedBody { - ann_pattern: $arena.alloc(*$ann_pattern), - ann_type: $arena.alloc(*$ann_type), - comment: $spaces_before_current - .first() - .and_then($crate::ast::CommentOrNewline::comment_str), - body_pattern: $arena.alloc($loc_pattern), - body_expr: *$arena.alloc($loc_def_expr), - }; - - ( - value_def, - roc_region::all::Region::span_across(&$ann_pattern.region, &$region), - ) - }}; -} - -// This is a macro only because trying to make it be a function caused lifetime issues. -#[macro_export] -macro_rules! join_alias_to_body { - ($arena:expr, $loc_pattern:expr, $loc_def_expr:expr, $header:expr, $ann_type:expr, $spaces_before_current:expr, $region:expr) => {{ - use roc_region::all::Region; - - // This is a case like - // UserId x : [UserId Int] - // UserId x = UserId 42 - // We optimistically parsed the first line as an alias; we now turn it - // into an annotation. - - let loc_name = $arena.alloc($header.name.map(|x| Pattern::Tag(x))); - let ann_pattern = Pattern::Apply(loc_name, $header.vars); - - let vars_region = Region::across_all($header.vars.iter().map(|v| &v.region)); - let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region); - let loc_ann_pattern = Loc::at(region_ann_pattern, ann_pattern); - - let value_def = ValueDef::AnnotatedBody { - ann_pattern: $arena.alloc(loc_ann_pattern), - ann_type: $arena.alloc(*$ann_type), - comment: $spaces_before_current - .first() - .and_then($crate::ast::CommentOrNewline::comment_str), - body_pattern: $arena.alloc($loc_pattern), - body_expr: *$arena.alloc($loc_def_expr), - }; - - ( - value_def, - Region::span_across(&$header.name.region, &$region), - ) - }}; -} - -fn parse_defs_end<'a>( - options: ExprParseOptions, - min_indent: u32, - mut defs: Defs<'a>, - arena: &'a Bump, - state: State<'a>, -) -> ParseResult<'a, Defs<'a>, EExpr<'a>> { - let mut global_state = state; - - loop { - // keep a copy in the event we get an EExpr::DefMissingFinalExpr - let state_before = global_state.clone(); - - let state = global_state; - - global_state = match parse_single_def(options, min_indent, arena, state) { - Ok((_, Some(single_def), next_state)) => { - let region = single_def.region; - let spaces_before_current = single_def.spaces_before; - let spaces_after_current = single_def.spaces_after; - - match single_def.type_or_value { - Either::First(type_def) => { - defs.push_type_def( - type_def, - region, - spaces_before_current, - spaces_after_current, - ); - } - Either::Second(value_def) => { - // If we got a ValueDef::Body, check if a type annotation preceded it. - // If so, we may need to combine them into an AnnotatedBody. - let joined_def = match value_def { - ValueDef::Body(loc_pattern, loc_def_expr) => { - let region = - Region::span_across(&loc_pattern.region, &loc_def_expr.region); - - let signature_must_match_value = spaces_before_current.len() <= 1; - let value_name = &loc_pattern.value; - - match defs.last() { - Some(Err(ValueDef::Annotation(ann_pattern, ann_type))) - if signature_must_match_value - || ann_pattern.value.equivalent(value_name) => - { - Some(join_ann_to_body!( - arena, - loc_pattern, - loc_def_expr, - ann_pattern, - ann_type, - spaces_before_current, - region - )) - } - Some(Ok(TypeDef::Alias { - header, - ann: ann_type, - })) if signature_must_match_value - || header - .vars - .first() - .map(|var| var.value.equivalent(value_name)) - .unwrap_or(false) => - { - Some(join_alias_to_body!( - arena, - loc_pattern, - loc_def_expr, - header, - ann_type, - spaces_before_current, - region - )) - } - _ => None, - } - } - _ => None, - }; - if let Some((joined_def, region)) = joined_def { - defs.replace_with_value_def(defs.tags.len() - 1, joined_def, region); - } else { - defs.push_value_def( - value_def, - region, - spaces_before_current, - spaces_after_current, - ); - } - } - } - - next_state - } - Ok((progress, None, s)) => return Ok((progress, defs, s)), - Err((MadeProgress, EExpr::DefMissingFinalExpr(..))) - | Err((MadeProgress, EExpr::DefMissingFinalExpr2(..))) => { - return Ok((MadeProgress, defs, state_before)) - } - Err((progress, err)) => return Err((progress, err)), - }; - } -} - -#[derive(Debug)] -pub struct SingleDef<'a> { - pub type_or_value: Either, ValueDef<'a>>, - pub region: Region, - pub spaces_before: &'a [CommentOrNewline<'a>], - pub spaces_after: &'a [CommentOrNewline<'a>], -} - -fn parse_defs_expr<'a>( - options: ExprParseOptions, - min_indent: u32, - defs: Defs<'a>, - arena: &'a Bump, - state: State<'a>, -) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { - match parse_defs_end(options, min_indent, defs, arena, state) { - Err(bad) => Err(bad), - Ok((_, def_state, state)) => { - // this is no def, because there is no `=` or `:`; parse as an expr - match space0_before_e(expr_start(options), EExpr::IndentEnd).parse( - arena, - state.clone(), - min_indent, - ) { - Err((_, fail)) => { - let mut def_state = def_state; - match def_state.pop_last_value() { - Some(loc_ret) => { - // If the poped value was the only item in defs - just return it as an expression - if def_state.is_empty() { - Ok((MadeProgress, loc_ret.value, state)) - } else { - Ok(( - MadeProgress, - Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)), - state, - )) - } - } - None => Err(( - MadeProgress, - EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()), - )), - } - } - Ok((_, loc_ret, state)) => Ok(( - MadeProgress, - Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)), - state, - )), - } - } - } -} - -fn alias_signature_with_space_before<'a>() -> impl Parser<'a, Loc>, EExpr<'a>> { - increment_min_indent(specialize_err( - EExpr::Type, - space0_before_e(type_annotation::located(false), EType::TIndentStart), - )) +fn alias_signature<'a>() -> impl Parser<'a, Loc>, EExpr<'a>> { + increment_min_indent(specialize_err(EExpr::Type, type_annotation::located(false))) } -fn opaque_signature_with_space_before<'a>() -> impl Parser< +fn opaque_signature<'a>() -> impl Parser< 'a, ( Loc>, @@ -1502,13 +1053,7 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser< EExpr<'a>, > { and( - specialize_err( - EExpr::Type, - space0_before_e( - type_annotation::located_opaque_signature(true), - EType::TIndentStart, - ), - ), + specialize_err(EExpr::Type, type_annotation::located_opaque_signature(true)), optional(backtrackable(specialize_err( EExpr::Type, space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart), @@ -1516,10 +1061,10 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser< ) } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] enum AliasOrOpaque { - Alias, - Opaque, + Alias, // ':' + Opaque, // ':=' } fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option> { @@ -1546,26 +1091,22 @@ fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option( - min_indent: u32, - options: ExprParseOptions, - expr_state: ExprState<'a>, - loc_op: Loc, +fn parse_stmt_alias_or_opaque<'a>( arena: &'a Bump, state: State<'a>, + min_indent: u32, + expr_state: ExprState<'a>, + kind: Loc, spaces_after_operator: &'a [CommentOrNewline<'a>], - kind: AliasOrOpaque, -) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { let expr_region = expr_state.expr.region; let indented_more = min_indent + 1; let (expr, arguments) = expr_state - .validate_is_type_def(arena, loc_op, kind) + .validate_is_type_def(arena, kind) .map_err(|fail| (MadeProgress, fail))?; - let mut defs = Defs::default(); - - let state = if let Some(tag) = extract_tag_and_spaces(arena, expr.value) { + let (res, state) = if let Some(tag) = extract_tag_and_spaces(arena, expr.value) { let name = tag.item; let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena); @@ -1586,12 +1127,13 @@ fn finish_parsing_alias_or_opaque<'a>( } } - match kind { + match kind.value { AliasOrOpaque::Alias => { - let (_, signature, state) = - alias_signature_with_space_before().parse(arena, state, min_indent)?; + let (_, signature, state) = alias_signature().parse(arena, state, min_indent)?; - let def_region = Region::span_across(&expr.region, &signature.region); + // TODO: this code used to be broken and it dropped the spaces after the operator. + // The formatter is not expecting this, so let's keep it as is for now. + // let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator)); let header = TypeHeader { name: Loc::at(expr.region, name), @@ -1603,16 +1145,16 @@ fn finish_parsing_alias_or_opaque<'a>( ann: signature, }; - defs.push_type_def(def, def_region, &[], &[]); - - state + (Stmt::TypeDef(def), state) } AliasOrOpaque::Opaque => { let (_, (signature, derived), state) = - opaque_signature_with_space_before().parse(arena, state, indented_more)?; + opaque_signature().parse(arena, state, indented_more)?; - let def_region = Region::span_across(&expr.region, &signature.region); + // TODO: this code used to be broken and it dropped the spaces after the operator. + // The formatter is not expecting this, so let's keep it as is for now. + // let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator)); let header = TypeHeader { name: Loc::at(expr.region, name), @@ -1625,9 +1167,7 @@ fn finish_parsing_alias_or_opaque<'a>( derived, }; - defs.push_type_def(def, def_region, &[], &[]); - - state + (Stmt::TypeDef(def), state) } } } else { @@ -1653,37 +1193,35 @@ fn finish_parsing_alias_or_opaque<'a>( .with_spaces_before(spaces_after_operator, ann_type.region); } - let def_region = Region::span_across(&call.region, &ann_type.region); - let value_def = ValueDef::Annotation(Loc::at(expr_region, good), ann_type); - defs.push_value_def(value_def, def_region, &[], &[]); - - state + (Stmt::ValueDef(value_def), state) } } } Err(_) => { // this `:`/`:=` likely occurred inline; treat it as an invalid operator - let op = match kind { + let op = match kind.value { AliasOrOpaque::Alias => ":", AliasOrOpaque::Opaque => ":=", }; - let fail = EExpr::BadOperator(op, loc_op.region.start()); + let fail = EExpr::BadOperator(op, kind.region.start()); return Err((MadeProgress, fail)); } } }; - parse_defs_expr(options, min_indent, defs, arena, state) + Ok((MadeProgress, res, state)) } mod ability { + use parser::absolute_indented_seq; + use super::*; use crate::{ ast::{AbilityMember, Spaceable, Spaced}, - parser::{absolute_indented_seq, EAbility}, + parser::EAbility, }; /// Parses a single ability demand line; see `parse_demand`. @@ -1794,7 +1332,7 @@ mod ability { } fn finish_parsing_ability_def_help<'a>( - start_column: u32, + call_min_indent: u32, name: Loc<&'a str>, args: &'a [Loc>], loc_implements: Loc>, @@ -1803,22 +1341,21 @@ fn finish_parsing_ability_def_help<'a>( ) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> { let mut demands = Vec::with_capacity_in(2, arena); - let min_indent_for_demand = start_column + 1; - // Parse the first demand. This will determine the indentation level all the // other demands must observe. let start = state.pos(); let (_, (demand_indent_level, first_demand), mut state) = - ability::parse_demand(ability::IndentLevel::PendingMin(min_indent_for_demand)) - .parse(arena, state, min_indent_for_demand) + ability::parse_demand(ability::IndentLevel::PendingMin(call_min_indent)) + .trace("ability_demand") + .parse(arena, state, call_min_indent) .map_err(|(progress, err)| (progress, EExpr::Ability(err, start)))?; demands.push(first_demand); let demand_indent = ability::IndentLevel::Exact(demand_indent_level); - let demand_parser = ability::parse_demand(demand_indent); + let demand_parser = ability::parse_demand(demand_indent).trace("ability_demand"); loop { - match demand_parser.parse(arena, state.clone(), min_indent_for_demand) { + match demand_parser.parse(arena, state.clone(), call_min_indent) { Ok((_, (_indent, demand), next_state)) => { state = next_state; demands.push(demand); @@ -1842,15 +1379,99 @@ fn finish_parsing_ability_def_help<'a>( Ok((MadeProgress, (type_def, def_region), state)) } +#[derive(Debug, Clone, Copy)] +pub enum Stmt<'a> { + Expr(Expr<'a>), + Backpassing(&'a [Loc>], &'a Loc>), + TypeDef(TypeDef<'a>), + ValueDef(ValueDef<'a>), +} + #[allow(clippy::too_many_arguments)] -fn parse_expr_operator<'a>( +fn parse_stmt_operator<'a>( + arena: &'a Bump, + state: State<'a>, min_indent: u32, + call_min_indent: u32, options: ExprParseOptions, - mut expr_state: ExprState<'a>, - loc_op: Loc, - line_indent: u32, + expr_state: ExprState<'a>, + loc_op: Loc, + initial_state: State<'a>, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + let (_, spaces_after_operator, state) = + loc_space0_e(EExpr::IndentEnd).parse(arena, state, min_indent)?; + + // a `-` is unary if it is preceded by a space and not followed by a space + + let op = loc_op.value; + let op_start = loc_op.region.start(); + let op_end = loc_op.region.end(); + let new_start = state.pos(); + match op { + OperatorOrDef::BinOp(BinOp::Minus) if expr_state.end != op_start && op_end == new_start => { + parse_negated_term( + arena, + state, + min_indent, + call_min_indent, + expr_state, + options, + initial_state, + loc_op.with_value(BinOp::Minus), + ) + .map(|(progress, expr, state)| (progress, Stmt::Expr(expr), state)) + } + OperatorOrDef::BinOp(op) => parse_after_binop( + arena, + state, + min_indent, + call_min_indent, + options, + true, + spaces_after_operator.value, + expr_state, + loc_op.with_value(op), + ) + .map(|(progress, expr, state)| (progress, Stmt::Expr(expr), state)), + OperatorOrDef::Assignment => parse_stmt_assignment( + arena, + state, + call_min_indent, + expr_state, + loc_op, + options, + spaces_after_operator, + ), + OperatorOrDef::Backpassing => parse_stmt_backpassing( + arena, + state, + call_min_indent, + expr_state, + loc_op, + options, + spaces_after_operator.value, + ), + OperatorOrDef::AliasOrOpaque(kind) => parse_stmt_alias_or_opaque( + arena, + state, + call_min_indent, + expr_state, + loc_op.with_value(kind), + spaces_after_operator.value, + ), + } +} + +#[allow(clippy::too_many_arguments)] +fn parse_expr_operator<'a>( arena: &'a Bump, state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + check_for_defs: bool, + expr_state: ExprState<'a>, + loc_op: Loc, initial_state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { let (_, spaces_after_operator, state) = @@ -1863,242 +1484,310 @@ fn parse_expr_operator<'a>( let op_end = loc_op.region.end(); let new_start = state.pos(); match op { - BinOp::Minus if expr_state.end != op_start && op_end == new_start => { - // negative terms + BinOp::Minus if expr_state.end != op_start && op_end == new_start => parse_negated_term( + arena, + state, + min_indent, + call_min_indent, + expr_state, + options, + initial_state, + loc_op, + ), + _ => parse_after_binop( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + spaces_after_operator, + expr_state, + loc_op, + ), + } +} - let (_, negated_expr, state) = loc_term(options).parse(arena, state, min_indent)?; +#[allow(clippy::too_many_arguments)] +fn parse_after_binop<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + check_for_defs: bool, + spaces_after_operator: &'a [CommentOrNewline], + mut expr_state: ExprState<'a>, + loc_op: Loc, +) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { + match loc_possibly_negative_or_negated_term(options).parse( + arena, + state.clone(), + call_min_indent, + ) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), + Ok((_, mut new_expr, state)) => { let new_end = state.pos(); - let arg = numeric_negate_expression( - arena, - initial_state, - loc_op, - negated_expr, - expr_state.spaces_after, - ); - let initial_state = state.clone(); - let (spaces, state) = - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => (&[] as &[_], state), - Ok((_, spaces, state)) => (spaces, state), - }; + // put the spaces from after the operator in front of the new_expr + if !spaces_after_operator.is_empty() { + new_expr = arena + .alloc(new_expr.value) + .with_spaces_before(spaces_after_operator, new_expr.region); + } - expr_state.arguments.push(arena.alloc(arg)); - expr_state.spaces_after = spaces; - expr_state.end = new_end; + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { + let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); - parse_expr_end(min_indent, options, expr_state, arena, state, initial_state) - } - BinOp::Assignment => { - let expr_region = expr_state.expr.region; + let call = to_call(arena, args, expr_state.expr); - let indented_more = line_indent + 1; + expr_state.operators.push((call, loc_op)); + expr_state.expr = new_expr; + expr_state.end = new_end; + expr_state.spaces_after = &[]; - let call = expr_state - .validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction) - .map_err(|fail| (MadeProgress, fail))?; + let expr = parse_expr_final(expr_state, arena); + Ok((MadeProgress, expr, state)) + } + Ok((_, spaces, state)) => { + let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); - let (value_def, def_region, state) = { - match expr_to_pattern_help(arena, &call.value) { - Ok(good) => { - let (_, mut body, state) = - expr_start(options).parse(arena, state, indented_more)?; + let call = to_call(arena, args, expr_state.expr); - // put the spaces from after the operator in front of the call - if !spaces_after_operator.is_empty() { - body = arena - .alloc(body.value) - .with_spaces_before(spaces_after_operator, body.region); - } + expr_state.operators.push((call, loc_op)); + expr_state.expr = new_expr; + expr_state.end = new_end; + expr_state.spaces_after = spaces; + + parse_expr_end( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + initial_state, + ) + } + } + } + Err((NoProgress, _e)) => { + return Err((MadeProgress, EExpr::TrailingOperator(state.pos()))); + } + } +} - let body_region = Region::span_across(&call.region, &body.region); +fn parse_stmt_backpassing<'a>( + arena: &'a Bump, + state: State<'a>, + call_min_indent: u32, + expr_state: ExprState<'a>, + loc_op: Loc, + options: ExprParseOptions, + spaces_after_operator: &'a [CommentOrNewline], +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + // called after parsing the <- operator - let alias = ValueDef::Body( - arena.alloc(Loc::at(expr_region, good)), - arena.alloc(body), - ); + let expr_region = expr_state.expr.region; - (alias, body_region, state) - } - Err(_) => { - // this `=` likely occurred inline; treat it as an invalid operator - let fail = EExpr::BadOperator(arena.alloc("="), loc_op.region.start()); + let call = expr_state + .validate_assignment_or_backpassing(arena, loc_op, |_, pos| EExpr::BadOperator("<-", pos)) + .map_err(|fail| (MadeProgress, fail))?; - return Err((MadeProgress, fail)); - } + let (loc_pattern, loc_body, state) = { + match expr_to_pattern_help(arena, &call.value) { + Ok(good) => { + let (_, mut ann_type, state) = + expr_start(options).parse(arena, state, call_min_indent)?; + + // put the spaces from after the operator in front of the call + if !spaces_after_operator.is_empty() { + ann_type = arena + .alloc(ann_type.value) + .with_spaces_before(spaces_after_operator, ann_type.region); } - }; - let mut defs = Defs::default(); - defs.push_value_def(value_def, def_region, &[], &[]); + (Loc::at(expr_region, good), ann_type, state) + } + Err(_) => { + // this `=` likely occurred inline; treat it as an invalid operator + let fail = EExpr::BadOperator("=", loc_op.region.start()); - parse_defs_expr(options, min_indent, defs, arena, state) + return Err((MadeProgress, fail)); + } } - BinOp::Backpassing => { - let expr_region = expr_state.expr.region; - let indented_more = min_indent + 1; - - let call = expr_state - .validate_assignment_or_backpassing(arena, loc_op, |_, pos| { - EExpr::BadOperator("<-", pos) - }) - .map_err(|fail| (MadeProgress, fail))?; - - let (loc_pattern, loc_body, state) = { - match expr_to_pattern_help(arena, &call.value) { - Ok(good) => { - let (_, mut ann_type, state) = - expr_start(options).parse(arena, state, indented_more)?; + }; - // put the spaces from after the operator in front of the call - if !spaces_after_operator.is_empty() { - ann_type = arena - .alloc(ann_type.value) - .with_spaces_before(spaces_after_operator, ann_type.region); - } + let ret = Stmt::Backpassing(arena.alloc([loc_pattern]), arena.alloc(loc_body)); + + Ok((MadeProgress, ret, state)) +} + +fn parse_stmt_multi_backpassing<'a>( + mut expr_state: ExprState<'a>, + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + options: ExprParseOptions, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + // called after parsing the first , in `a, b <- c` (e.g.) + + let (_, mut patterns, state) = specialize_err_ref( + EExpr::Pattern, + crate::parser::sep_by0( + byte(b',', EPattern::Start), + space0_around_ee( + crate::pattern::loc_pattern_help(), + EPattern::Start, + EPattern::IndentEnd, + ), + ), + ) + .parse(arena, state, min_indent) + .map_err(|(progress, err)| { + // We were expecting the end of an expression, and parsed a comma + // therefore we are either on the LHS of backpassing or this is was + // in an invalid position. + if let EExpr::Pattern(EPattern::IndentEnd(_), pos) = err { + (progress, EExpr::UnexpectedComma(pos.sub(1))) + } else { + (progress, err) + } + })?; - (Loc::at(expr_region, good), ann_type, state) - } - Err(_) => { - // this `=` likely occurred inline; treat it as an invalid operator - let fail = EExpr::BadOperator("=", loc_op.region.start()); + expr_state.consume_spaces(arena); + let call = to_call(arena, expr_state.arguments, expr_state.expr); - return Err((MadeProgress, fail)); - } - } - }; + let pattern = expr_to_pattern_help(arena, &call.value).map_err(|()| { + ( + MadeProgress, + EExpr::Pattern(arena.alloc(EPattern::NotAPattern(state.pos())), state.pos()), + ) + })?; - let parse_cont = space0_before_e(expr_start(options), EExpr::IndentEnd); + let loc_pattern = Loc::at(call.region, pattern); - let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?; + patterns.insert(0, loc_pattern); - let ret = Expr::Backpassing( - arena.alloc([loc_pattern]), - arena.alloc(loc_body), - arena.alloc(loc_cont), - ); + let line_indent = state.line_indent(); - Ok((MadeProgress, ret, state)) - } - BinOp::IsAliasType | BinOp::IsOpaqueType => finish_parsing_alias_or_opaque( - min_indent, - options, - expr_state, - loc_op, - arena, - state, - spaces_after_operator, - match op { - BinOp::IsAliasType => AliasOrOpaque::Alias, - BinOp::IsOpaqueType => AliasOrOpaque::Opaque, - _ => unreachable!(), - }, - ), - _ => match loc_possibly_negative_or_negated_term(options).parse( - arena, - state.clone(), - min_indent, - ) { - Err((MadeProgress, f)) => Err((MadeProgress, f)), - Ok((_, mut new_expr, state)) => { - let new_end = state.pos(); + match two_bytes(b'<', b'-', EExpr::BackpassArrow).parse(arena, state.clone(), min_indent) { + Err((_, fail)) => Err((MadeProgress, fail)), + Ok((_, _, state)) => { + let parse_body = space0_before_e(expr_start(options), EExpr::IndentEnd); - let initial_state = state.clone(); + let (_, loc_body, state) = parse_body.parse(arena, state, line_indent + 1)?; - // put the spaces from after the operator in front of the new_expr - if !spaces_after_operator.is_empty() { - new_expr = arena - .alloc(new_expr.value) - .with_spaces_before(spaces_after_operator, new_expr.region); - } + let ret = Stmt::Backpassing(patterns.into_bump_slice(), arena.alloc(loc_body)); - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => { - let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); + Ok((MadeProgress, ret, state)) + } + } +} - let call = to_call(arena, args, expr_state.expr); +fn parse_stmt_assignment<'a>( + arena: &'a Bump, + state: State<'a>, + call_min_indent: u32, + expr_state: ExprState<'a>, + loc_op: Loc, + options: ExprParseOptions, + spaces_after_operator: Loc<&'a [CommentOrNewline]>, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + let call = expr_state + .validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction) + .map_err(|fail| (MadeProgress, fail))?; - expr_state.operators.push((call, loc_op)); - expr_state.expr = new_expr; - expr_state.end = new_end; - expr_state.spaces_after = &[]; + let (value_def, state) = { + match expr_to_pattern_help(arena, &call.value) { + Ok(good) => { + let (_, body, state) = parse_block_inner( + options, + arena, + state, + call_min_indent, + EExpr::IndentEnd, + |a, _| a.clone(), + spaces_after_operator, + !spaces_after_operator.value.is_empty(), + )?; - let expr = parse_expr_final(expr_state, arena); - Ok((MadeProgress, expr, state)) - } - Ok((_, spaces, state)) => { - let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); + let alias = + ValueDef::Body(arena.alloc(Loc::at(call.region, good)), arena.alloc(body)); - let call = to_call(arena, args, expr_state.expr); + (alias, state) + } + Err(_) => { + // this `=` likely occurred inline; treat it as an invalid operator + let fail = EExpr::BadOperator(arena.alloc("="), loc_op.region.start()); - expr_state.operators.push((call, loc_op)); - expr_state.expr = new_expr; - expr_state.end = new_end; - expr_state.spaces_after = spaces; + return Err((MadeProgress, fail)); + } + } + }; - let new_min_indent = if is_expr_suffixed(&new_expr.value) { - min_indent + 1 - } else { - min_indent - }; + Ok((MadeProgress, Stmt::ValueDef(value_def), state)) +} - match parse_expr_end( - new_min_indent, - options, - expr_state, - arena, - state, - initial_state, - ) { - Ok((progress, expr, state)) => { - if let Expr::BinOps(..) = expr { - let def_region = expr.get_region_spanning_binops(); - let mut new_expr = Loc::at(def_region, expr); - - if is_expr_suffixed(&new_expr.value) { - // We have parsed a statement such as `"hello" |> line!` - // put the spaces from after the operator in front of the call - if !spaces_after_operator.is_empty() { - new_expr = arena.alloc(expr).with_spaces_before( - spaces_after_operator, - def_region, - ); - } +#[allow(clippy::too_many_arguments)] +fn parse_negated_term<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + mut expr_state: ExprState<'a>, + options: ExprParseOptions, + initial_state: State<'a>, + loc_op: Loc, +) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { + let (_, negated_expr, state) = loc_term(options).parse(arena, state, min_indent)?; + let new_end = state.pos(); - let value_def = ValueDef::Stmt(arena.alloc(new_expr)); + let arg = numeric_negate_expression( + arena, + initial_state, + loc_op, + negated_expr, + expr_state.spaces_after, + ); - let mut defs = Defs::default(); - defs.push_value_def(value_def, def_region, &[], &[]); + let initial_state = state.clone(); - return parse_defs_expr( - options, min_indent, defs, arena, state, - ); - } - } + let (spaces, state) = match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => (&[] as &[_], state), + Ok((_, spaces, state)) => (spaces, state), + }; - // else return the parsed expression - Ok((progress, expr, state)) - } - Err(err) => Err(err), - } - } - } - } - Err((NoProgress, _e)) => { - return Err((MadeProgress, EExpr::TrailingOperator(state.pos()))); - } - }, - } + expr_state.arguments.push(arena.alloc(arg)); + expr_state.spaces_after = spaces; + expr_state.end = new_end; + + // TODO: this should probably be handled in the caller, not here + parse_expr_end( + arena, + state, + min_indent, + call_min_indent, + options, + true, + expr_state, + initial_state, + ) } +#[allow(clippy::too_many_arguments)] fn parse_expr_end<'a>( + arena: &'a Bump, + state: State<'a>, min_indent: u32, + call_min_indent: u32, options: ExprParseOptions, + check_for_defs: bool, mut expr_state: ExprState<'a>, - arena: &'a Bump, - state: State<'a>, initial_state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { let parser = skip_first( @@ -2106,206 +1795,215 @@ fn parse_expr_end<'a>( loc_term_or_underscore(options), ); - match parser.parse(arena, state.clone(), min_indent) { + match parser.parse(arena, state.clone(), call_min_indent) { Err((MadeProgress, f)) => Err((MadeProgress, f)), - Ok(( - _, - implements @ Loc { - value: - Expr::Var { - module_name: "", - ident: crate::keyword::IMPLEMENTS, - .. - }, - .. - }, + Ok((_, arg, state)) => parse_apply_arg( + arena, state, - )) if matches!(expr_state.expr.value, Expr::Tag(..)) => { - // This is an ability definition, `Ability arg1 ... implements ...`. - - let name = expr_state.expr.map_owned(|e| match e { - Expr::Tag(name) => name, - _ => unreachable!(), - }); - - let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena); - for argument in expr_state.arguments { - match expr_to_pattern_help(arena, &argument.value) { - Ok(good) => { - arguments.push(Loc::at(argument.region, good)); - } - Err(_) => { - let start = argument.region.start(); - let err = &*arena.alloc(EPattern::Start(start)); - return Err((MadeProgress, EExpr::Pattern(err, argument.region.start()))); - } - } - } - - // Attach any spaces to the `implements` keyword - let implements = if !expr_state.spaces_after.is_empty() { - arena - .alloc(Implements::Implements) - .with_spaces_before(expr_state.spaces_after, implements.region) - } else { - Loc::at(implements.region, Implements::Implements) - }; - - let args = arguments.into_bump_slice(); - let (_, (type_def, def_region), state) = - finish_parsing_ability_def_help(min_indent, name, args, implements, arena, state)?; - - let mut defs = Defs::default(); - - defs.push_type_def(type_def, def_region, &[], &[]); - - parse_defs_expr(options, min_indent, defs, arena, state) - } - Ok((_, mut arg, state)) => { - let new_end = state.pos(); - - let min_indent = if is_expr_suffixed(&arg.value) { - min_indent + 1 - } else { - min_indent - }; - - // now that we have `function arg1 ... argn`, attach the spaces to the `argn` - if !expr_state.spaces_after.is_empty() { - arg = arena - .alloc(arg.value) - .with_spaces_before(expr_state.spaces_after, arg.region); - - expr_state.spaces_after = &[]; - } - let initial_state = state.clone(); - - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => { - expr_state.arguments.push(arena.alloc(arg)); - expr_state.end = new_end; - expr_state.spaces_after = &[]; - - let expr = parse_expr_final(expr_state, arena); - Ok((MadeProgress, expr, state)) - } - Ok((_, new_spaces, state)) => { - expr_state.arguments.push(arena.alloc(arg)); - expr_state.end = new_end; - expr_state.spaces_after = new_spaces; - - parse_expr_end(min_indent, options, expr_state, arena, state, initial_state) - } - } - } + min_indent, + call_min_indent, + expr_state, + arg, + options, + check_for_defs, + ), Err((NoProgress, _)) => { let before_op = state.clone(); // try an operator - let line_indent = state.line_indent(); - match loc(operator()).parse(arena, state.clone(), min_indent) { + match loc(bin_op(check_for_defs)).parse(arena, state.clone(), min_indent) { Err((MadeProgress, f)) => Err((MadeProgress, f)), Ok((_, loc_op, state)) => { expr_state.consume_spaces(arena); let initial_state = before_op; parse_expr_operator( + arena, + state, min_indent, + call_min_indent, options, + check_for_defs, expr_state, loc_op, - line_indent, - arena, - state, initial_state, ) } Err((NoProgress, _)) => { - let mut state = state; - // try multi-backpassing - if options.accept_multi_backpassing && state.bytes().starts_with(b",") { - state = state.advance(1); - - let (_, mut patterns, state) = specialize_err_ref( - EExpr::Pattern, - crate::parser::sep_by0( - byte(b',', EPattern::Start), - space0_around_ee( - crate::pattern::loc_pattern_help(), - EPattern::Start, - EPattern::IndentEnd, - ), - ), - ) - .parse(arena, state, min_indent) - .map_err(|(progress, err)| { - // We were expecting the end of an expression, and parsed a comma - // therefore we are either on the LHS of backpassing or this is was - // in an invalid position. - if let EExpr::Pattern(EPattern::IndentEnd(_), pos) = err { - (progress, EExpr::UnexpectedComma(pos.sub(1))) - } else { - (progress, err) - } - })?; + let expr = parse_expr_final(expr_state, arena); + // roll back space parsing + Ok((MadeProgress, expr, initial_state)) + } + } + } + } +} - expr_state.consume_spaces(arena); - let call = to_call(arena, expr_state.arguments, expr_state.expr); +fn parse_stmt_after_apply<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + mut expr_state: ExprState<'a>, + options: ExprParseOptions, + initial_state: State<'a>, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + let before_op = state.clone(); + match loc(operator()).parse(arena, state.clone(), min_indent) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), + Ok((_, loc_op, state)) => { + expr_state.consume_spaces(arena); + let initial_state = before_op; + parse_stmt_operator( + arena, + state, + min_indent, + call_min_indent, + options, + expr_state, + loc_op, + initial_state, + ) + } + Err((NoProgress, _)) => { + let mut state = state; + // try multi-backpassing + if options.accept_multi_backpassing && state.bytes().starts_with(b",") { + state = state.advance(1); + + parse_stmt_multi_backpassing(expr_state, arena, state, min_indent, options) + } else if options.check_for_arrow && state.bytes().starts_with(b"->") { + Err((MadeProgress, EExpr::BadOperator("->", state.pos()))) + } else { + let expr = parse_expr_final(expr_state, arena); - let pattern = expr_to_pattern_help(arena, &call.value).map_err(|()| { - ( - MadeProgress, - EExpr::Pattern( - arena.alloc(EPattern::NotAPattern(state.pos())), - state.pos(), - ), - ) - })?; + // roll back space parsing + Ok((MadeProgress, Stmt::Expr(expr), initial_state)) + } + } + } +} - let loc_pattern = Loc::at(call.region, pattern); +#[allow(clippy::too_many_arguments)] +fn parse_apply_arg<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + mut expr_state: ExprState<'a>, + mut arg: Loc>, + options: ExprParseOptions, + check_for_defs: bool, +) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { + let new_end = state.pos(); - patterns.insert(0, loc_pattern); + // now that we have `function arg1 ... argn`, attach the spaces to the `argn` + if !expr_state.spaces_after.is_empty() { + arg = arena + .alloc(arg.value) + .with_spaces_before(expr_state.spaces_after, arg.region); - match two_bytes(b'<', b'-', EExpr::BackpassArrow).parse( - arena, - state.clone(), - min_indent, - ) { - Err((_, fail)) => Err((MadeProgress, fail)), - Ok((_, _, state)) => { - let parse_body = space0_before_e( - increment_min_indent(expr_start(options)), - EExpr::IndentEnd, - ); - - let (_, loc_body, state) = - parse_body.parse(arena, state, min_indent)?; - - let parse_cont = - space0_before_e(expr_start(options), EExpr::IndentEnd); - - let (_, loc_cont, state) = - parse_cont.parse(arena, state, min_indent)?; - - let ret = Expr::Backpassing( - patterns.into_bump_slice(), - arena.alloc(loc_body), - arena.alloc(loc_cont), - ); - - Ok((MadeProgress, ret, state)) - } - } - } else if options.check_for_arrow && state.bytes().starts_with(b"->") { - Err((MadeProgress, EExpr::BadOperator("->", state.pos()))) - } else { - let expr = parse_expr_final(expr_state, arena); + expr_state.spaces_after = &[]; + } + let initial_state = state.clone(); - // roll back space parsing - Ok((MadeProgress, expr, initial_state)) - } - } + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = &[]; + + let expr = parse_expr_final(expr_state, arena); + Ok((MadeProgress, expr, state)) + } + Ok((_, new_spaces, state)) => { + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = new_spaces; + + parse_expr_end( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + initial_state, + ) + } + } +} + +fn parse_ability_def<'a>( + expr_state: ExprState<'a>, + state: State<'a>, + arena: &'a Bump, + implements: Loc>, + call_min_indent: u32, +) -> Result<(TypeDef<'a>, State<'a>), (Progress, EExpr<'a>)> { + // This is an ability definition, `Ability arg1 ... implements ...`. + + let name = expr_state.expr.map_owned(|e| match e { + Expr::Tag(name) => name, + _ => unreachable!(), + }); + + let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena); + for argument in expr_state.arguments { + match expr_to_pattern_help(arena, &argument.value) { + Ok(good) => { + arguments.push(Loc::at(argument.region, good)); + } + Err(_) => { + let start = argument.region.start(); + let err = &*arena.alloc(EPattern::Start(start)); + return Err((MadeProgress, EExpr::Pattern(err, argument.region.start()))); } } } + + // Attach any spaces to the `implements` keyword + let implements = if !expr_state.spaces_after.is_empty() { + arena + .alloc(Implements::Implements) + .with_spaces_before(expr_state.spaces_after, implements.region) + } else { + Loc::at(implements.region, Implements::Implements) + }; + + let args = arguments.into_bump_slice(); + let (_, (type_def, _), state) = + finish_parsing_ability_def_help(call_min_indent, name, args, implements, arena, state)?; + + Ok((type_def, state)) +} + +pub fn loc_expr_block<'a>( + accept_multi_backpassing: bool, +) -> impl Parser<'a, Loc>, EExpr<'a>> { + space0_after_e( + move |arena: &'a Bump, state: State<'a>, min_indent: u32| { + let options = ExprParseOptions { + accept_multi_backpassing, + check_for_arrow: true, + }; + + let (_, loc_first_space, state) = + loc_space0_e(EExpr::IndentStart).parse(arena, state, min_indent)?; + + parse_block_inner( + options, + arena, + state, + min_indent, + EExpr::IndentStart, + |a, _| a.clone(), + loc_first_space, + true, + ) + }, + EExpr::IndentEnd, + ) + .trace("loc_expr_block") } pub fn loc_expr<'a>(accept_multi_backpassing: bool) -> impl Parser<'a, Loc>, EExpr<'a>> { @@ -2507,32 +2205,39 @@ fn assigned_expr_field_to_pattern_help<'a>( pub fn parse_top_level_defs<'a>( arena: &'a bumpalo::Bump, state: State<'a>, - mut output: Defs<'a>, + output: Defs<'a>, ) -> ParseResult<'a, Defs<'a>, EExpr<'a>> { - let (_, initial_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, 0)?; + let (_, loc_first_space, state) = loc_space0_e(EExpr::IndentStart).parse(arena, state, 0)?; - let start_column = state.column(); + let (_, stmts, state) = parse_stmt_seq( + arena, + state, + |e, _| e.clone(), + ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }, + 0, + loc_first_space, + EExpr::IndentEnd, + )?; - let options = ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }; + let (_, last_space, state) = space0_e(EExpr::IndentStart).parse(arena, state, 0)?; let existing_len = output.tags.len(); - let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied()); + let (mut output, last_expr) = + stmts_to_defs(&stmts, output, false, arena).map_err(|e| (MadeProgress, e))?; - let (_, mut output, state) = parse_defs_end(options, start_column, output, arena, state)?; - - let (_, final_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, start_column)?; + if let Some(expr) = last_expr { + return Err(( + MadeProgress, + EExpr::UnexpectedTopLevelExpr(expr.region.start()), + )); + } if output.tags.len() > existing_len { - // add surrounding whitespace - let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied()); - - debug_assert!(output.space_before[existing_len].is_empty()); - output.space_before[existing_len] = before; - + let after = Slice::extend_new(&mut output.spaces, last_space.iter().copied()); let last = output.tags.len() - 1; debug_assert!(output.space_after[last].is_empty() || after.is_empty()); output.space_after[last] = after; @@ -2568,10 +2273,7 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo // Parse the -> which separates params from body two_bytes(b'-', b'>', EClosure::Arrow), // Parse the body - space0_before_e( - specialize_err_ref(EClosure::Body, expr_start(options)), - EClosure::IndentBody, - ), + block(options, true, EClosure::IndentBody, EClosure::Body), ), ), ), @@ -2584,11 +2286,13 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo } mod when { + use parser::indented_seq_skip_first; + use super::*; - use crate::ast::WhenBranch; + use crate::{ast::WhenBranch, blankspace::space0_around_e_no_after_indent_check}; /// Parser for when expressions. - pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> { + pub fn when_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> { map_with_arena( and( indented_seq_skip_first( @@ -2610,7 +2314,7 @@ mod when { move |arena: &'a Bump, (loc_condition, branches): (Loc>, Vec<'a, &'a WhenBranch<'a>>)| { Expr::When(arena.alloc(loc_condition), branches.into_bump_slice()) } - ) + ).trace("when") } fn branches<'a>( @@ -2718,6 +2422,16 @@ mod when { ) } + fn error_on_arrow<'a, T, E: 'a>(f: impl Fn(Position) -> E) -> impl Parser<'a, T, E> { + move |_, state: State<'a>, _| { + if state.bytes().starts_with(b"->") { + Err((MadeProgress, f(state.pos()))) + } else { + Err((NoProgress, f(state.pos()))) + } + } + } + fn branch_single_alternative<'a>() -> impl Parser<'a, Loc>, EWhen<'a>> { move |arena, state, min_indent| { let (_, spaces, state) = @@ -2754,8 +2468,7 @@ mod when { Ok((_progress, spaces, state)) => { match pattern_indent_level { Some(wanted) if state.column() > wanted => { - // this branch is indented too much - Err((NoProgress, EWhen::IndentPattern(state.pos()))) + error_on_arrow(EWhen::IndentPattern).parse(arena, state, min_indent) } Some(wanted) if state.column() < wanted => { let indent = wanted - state.column(); @@ -2800,13 +2513,14 @@ mod when { /// Parsing the righthandside of a branch in a when conditional. fn branch_result<'a>(indent: u32) -> impl Parser<'a, Loc>, EWhen<'a>> { + let options = ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }; move |arena, state, _min_indent| { skip_first( two_bytes(b'-', b'>', EWhen::Arrow), - space0_before_e( - specialize_err_ref(EWhen::Branch, loc_expr(true)), - EWhen::IndentBranch, - ), + block(options, true, EWhen::IndentBranch, EWhen::Branch), ) .parse(arena, state, indent) } @@ -2814,6 +2528,10 @@ mod when { } fn if_branch<'a>() -> impl Parser<'a, (Loc>, Loc>), EIf<'a>> { + let options = ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }; skip_second( and( skip_second( @@ -2824,86 +2542,95 @@ fn if_branch<'a>() -> impl Parser<'a, (Loc>, Loc>), EIf<'a>> { ), parser::keyword(keyword::THEN, EIf::Then), ), - space0_around_ee( - specialize_err_ref(EIf::ThenBranch, loc_expr(true)), - EIf::IndentThenBranch, - EIf::IndentElseToken, + map_with_arena( + space0_after_e( + block(options, false, EIf::IndentThenBranch, EIf::ThenBranch), + EIf::IndentElseToken, + ), + |arena: &'a Bump, block: Loc>| match block.value { + Expr::SpaceAfter(&Expr::SpaceBefore(x, before), after) => block.with_value( + Expr::SpaceBefore(arena.alloc(Expr::SpaceAfter(x, after)), before), + ), + _ => block, + }, ), ), parser::keyword(keyword::ELSE, EIf::Else), ) } -fn expect_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<'a>> { +fn expect_help<'a>( + options: ExprParseOptions, + preceding_comment: Region, +) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> { move |arena: &'a Bump, state: State<'a>, min_indent| { - let start_column = state.column(); + let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect); + let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect); + let parse_expect = either(parse_expect_vanilla, parse_expect_fx); - let (_, _, state) = - parser::keyword(keyword::EXPECT, EExpect::Expect).parse(arena, state, min_indent)?; + let (_, kw, state) = parse_expect.parse(arena, state, min_indent)?; - let (_, condition, state) = space0_before_e( - specialize_err_ref( - EExpect::Condition, - set_min_indent(start_column + 1, expr_start(options)), - ), + let (_, condition, state) = parse_block( + options, + arena, + state, + true, EExpect::IndentCondition, + EExpect::Condition, ) - .parse(arena, state, start_column + 1) .map_err(|(_, f)| (MadeProgress, f))?; - let parse_cont = specialize_err_ref( - EExpect::Continuation, - space0_before_e(expr_start(options), EExpr::IndentEnd), - ); - - let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?; - - let expr = Expr::Expect(arena.alloc(condition), arena.alloc(loc_cont)); + let vd = match kw { + Either::First(_) => ValueDef::Expect { + condition: arena.alloc(condition), + preceding_comment, + }, + Either::Second(_) => ValueDef::ExpectFx { + condition: arena.alloc(condition), + preceding_comment, + }, + }; - Ok((MadeProgress, expr, state)) + Ok((MadeProgress, Stmt::ValueDef(vd), state)) } } -fn dbg_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<'a>> { - move |arena: &'a Bump, state: State<'a>, min_indent| { - let start_column = state.column(); - +fn dbg_help<'a>( + options: ExprParseOptions, + preceding_comment: Region, +) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> { + (move |arena: &'a Bump, state: State<'a>, min_indent| { let (_, _, state) = parser::keyword(keyword::DBG, EExpect::Dbg).parse(arena, state, min_indent)?; - let (_, condition, state) = space0_before_e( - specialize_err_ref( - EExpect::Condition, - set_min_indent(start_column + 1, expr_start(options)), - ), + let (_, condition, state) = parse_block( + options, + arena, + state, + true, EExpect::IndentCondition, + EExpect::Condition, ) - .parse(arena, state, start_column + 1) .map_err(|(_, f)| (MadeProgress, f))?; - let parse_cont = specialize_err_ref( - EExpect::Continuation, - space0_before_e(expr_start(options), EExpr::IndentEnd), - ); - - let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?; - - let expr = Expr::Dbg(arena.alloc(condition), arena.alloc(loc_cont)); + let stmt = Stmt::ValueDef(ValueDef::Dbg { + condition: arena.alloc(condition), + preceding_comment, + }); - Ok((MadeProgress, expr, state)) - } + Ok((MadeProgress, stmt, state)) + }) + .trace("dbg_help") } -fn import_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { - move |arena: &'a Bump, state: State<'a>, min_indent: u32| { - let (_, (import_def, spaces_after), state) = - specialize_err(EExpr::Import, import()).parse(arena, state, min_indent)?; - - let mut defs = Defs::default(); - defs.push_value_def(import_def.value, import_def.region, &[], spaces_after); - - parse_defs_expr(options, min_indent, defs, arena, state) - } +fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> { + skip_second( + skip_first( + parser::keyword(keyword::IMPORT, EImport::Import), + increment_min_indent(one_of!(import_body(), import_ingested_file_body())), + ), + require_newline_or_eof(EImport::EndNewline), + ) } fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<'a>> { @@ -2937,12 +2664,14 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< } }; - let (_, else_branch, state) = space0_before_e( - specialize_err_ref(EIf::ElseBranch, expr_start(options)), + let (_, else_branch, state) = parse_block( + options, + arena, + state_final_else, + true, EIf::IndentElseBranch, - ) - .parse(arena, state_final_else, min_indent) - .map_err(|(_, f)| (MadeProgress, f))?; + EIf::ElseBranch, + )?; let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch)); @@ -2950,6 +2679,486 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< } } +fn block<'a, E>( + options: ExprParseOptions, + require_indent: bool, + indent_problem: fn(Position) -> E, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, +) -> impl Parser<'a, Loc>, E> +where + E: 'a + SpaceProblem, +{ + (move |arena: &'a Bump, state, _min_indent| { + parse_block( + options, + arena, + state, + require_indent, + indent_problem, + wrap_error, + ) + }) + .trace("block") +} + +fn parse_block<'a, E>( + options: ExprParseOptions, + arena: &'a Bump, + state: State<'a>, + require_indent: bool, + indent_problem: fn(Position) -> E, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, +) -> ParseResult<'a, Loc>, E> +where + E: 'a + SpaceProblem, +{ + let min_indent = if require_indent { + state.line_indent() + 1 + } else { + 0 + }; + + let (_, loc_first_space, state) = + loc_space0_e(indent_problem).parse(arena, state, min_indent)?; + + let allow_defs = !loc_first_space.value.is_empty(); + + parse_block_inner( + options, + arena, + state, + min_indent, + indent_problem, + wrap_error, + loc_first_space, + allow_defs, + ) +} + +#[allow(clippy::too_many_arguments)] +fn parse_block_inner<'a, E>( + options: ExprParseOptions, + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + indent_problem: fn(Position) -> E, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, + first_space: Loc<&'a [CommentOrNewline<'a>]>, + allow_defs: bool, +) -> ParseResult<'a, Loc>, E> +where + E: 'a + SpaceProblem, +{ + if allow_defs { + let (_, stmts, state) = parse_stmt_seq( + arena, + state, + wrap_error, + options, + min_indent, + Loc::at(first_space.region, &[]), + indent_problem, + )?; + + if stmts.is_empty() { + return Err(( + NoProgress, + wrap_error(arena.alloc(EExpr::Start(state.pos())), state.pos()), + )); + } + + let last_pos = state.pos(); + + let loc_expr = stmts_to_expr(&stmts, arena) + .map_err(|e| (MadeProgress, wrap_error(arena.alloc(e), last_pos)))?; + + let loc_expr = if first_space.value.is_empty() { + loc_expr + } else { + arena + .alloc(loc_expr.value) + .with_spaces_before(first_space.value, loc_expr.region) + }; + + Ok((MadeProgress, loc_expr, state)) + } else { + let (p2, loc_expr, state) = + specialize_err_ref(wrap_error, expr_start(options)).parse(arena, state, min_indent)?; + + let loc_expr = if first_space.value.is_empty() { + loc_expr + } else { + arena + .alloc(loc_expr.value) + .with_spaces_before(first_space.value, loc_expr.region) + }; + + Ok((p2, loc_expr, state)) + } +} + +fn parse_stmt_seq<'a, E: SpaceProblem + 'a>( + arena: &'a Bump, + mut state: State<'a>, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, + options: ExprParseOptions, + min_indent: u32, + mut last_space: Loc<&'a [CommentOrNewline<'a>]>, + indent_problem: fn(Position) -> E, +) -> ParseResult<'a, Vec<'a, SpacesBefore<'a, Loc>>>, E> { + let mut stmts = Vec::new_in(arena); + let mut state_before_space = state.clone(); + loop { + if at_terminator(&state) { + state = state_before_space; + break; + } + + let loc_stmt = match specialize_err_ref(wrap_error, stmt_start(options, last_space.region)) + .parse(arena, state.clone(), min_indent) + { + Ok((_p, s, new_state)) => { + state_before_space = new_state.clone(); + state = new_state; + s + } + Err((NoProgress, _)) => { + if stmts.is_empty() { + return Err(( + NoProgress, + wrap_error(arena.alloc(EExpr::Start(state.pos())), state.pos()), + )); + } + + state = state_before_space; + break; + } + Err((MadeProgress, e)) => { + return Err((MadeProgress, e)); + } + }; + + stmts.push(SpacesBefore { + before: last_space.value, + item: loc_stmt, + }); + + match loc_space0_e(indent_problem).parse(arena, state.clone(), min_indent) { + Ok((_p, s_loc, new_state)) => { + if s_loc.value.is_empty() { + // require a newline or a terminator after the statement + if at_terminator(&new_state) { + state = state_before_space; + break; + } + + return Err(( + MadeProgress, + wrap_error(arena.alloc(EExpr::BadExprEnd(state.pos())), state.pos()), + )); + } + last_space = s_loc; + state = new_state; + } + Err(_) => { + break; + } + }; + } + Ok((MadeProgress, stmts, state)) +} + +fn at_terminator(state: &State<'_>) -> bool { + matches!( + state.bytes().first(), + None | Some(b']' | b'}' | b')' | b',') + ) +} + +fn stmts_to_expr<'a>( + stmts: &[SpacesBefore<'a, Loc>>], + arena: &'a Bump, +) -> Result>, EExpr<'a>> { + if stmts.len() > 1 { + let first_pos = stmts.first().unwrap().item.region.start(); + let last_pos = stmts.last().unwrap().item.region.end(); + + let (defs, last_expr) = stmts_to_defs(stmts, Defs::default(), true, arena)?; + + let final_expr = match last_expr { + Some(e) => e, + None => return Err(EExpr::DefMissingFinalExpr(last_pos)), + }; + + let region = Region::new(first_pos, last_pos); + + if defs.is_empty() { + Ok(final_expr) + } else { + Ok(Loc::at( + region, + Expr::Defs(arena.alloc(defs), arena.alloc(final_expr)), + )) + } + } else { + let SpacesBefore { + before: space, + item: loc_stmt, + } = *stmts.last().unwrap(); + let expr = match loc_stmt.value { + Stmt::Expr(e) => { + if space.is_empty() { + e + } else { + arena.alloc(e).before(space) + } + } + Stmt::ValueDef(ValueDef::Dbg { .. }) => { + return Err(EExpr::Dbg( + EExpect::Continuation( + arena.alloc(EExpr::IndentEnd(loc_stmt.region.end())), + loc_stmt.region.end(), + ), + loc_stmt.region.start(), + )); + } + Stmt::ValueDef(ValueDef::Expect { .. }) => { + return Err(EExpr::Expect( + EExpect::Continuation( + arena.alloc(EExpr::IndentEnd(loc_stmt.region.end())), + loc_stmt.region.end(), + ), + loc_stmt.region.start(), + )); + } + Stmt::Backpassing(..) | Stmt::TypeDef(_) | Stmt::ValueDef(_) => { + return Err(EExpr::IndentEnd(loc_stmt.region.end())) + } + }; + + Ok(loc_stmt.with_value(expr)) + } +} + +fn stmts_to_defs<'a>( + stmts: &[SpacesBefore<'a, Loc>>], + mut defs: Defs<'a>, + exprify_dbg: bool, + arena: &'a Bump, +) -> Result<(Defs<'a>, Option>>), EExpr<'a>> { + let mut last_expr = None; + let mut i = 0; + while i < stmts.len() { + let sp_stmt = stmts[i]; + match sp_stmt.item.value { + Stmt::Expr(e) => { + if is_expr_suffixed(&e) && i + 1 < stmts.len() { + defs.push_value_def( + ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))), + sp_stmt.item.region, + sp_stmt.before, + &[], + ); + } else { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + last_expr = Some(sp_stmt.item.with_value(e)); + } + } + Stmt::Backpassing(pats, call) => { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + if i + 1 >= stmts.len() { + return Err(EExpr::BackpassContinue(sp_stmt.item.region.end())); + } + + let rest = stmts_to_expr(&stmts[i + 1..], arena)?; + + let e = Expr::Backpassing(arena.alloc(pats), arena.alloc(call), arena.alloc(rest)); + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + let region = Region::new(sp_stmt.item.region.start(), rest.region.end()); + + last_expr = Some(Loc::at(region, e)); + + // don't re-process the rest of the statements; they got consumed by the backpassing + break; + } + + Stmt::TypeDef(td) => { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + if let ( + TypeDef::Alias { + header, + ann: ann_type, + }, + Some(( + spaces_middle, + Stmt::ValueDef(ValueDef::Body(loc_pattern, loc_def_expr)), + )), + ) = (td, stmts.get(i + 1).map(|s| (s.before, s.item.value))) + { + if spaces_middle.len() <= 1 + || header + .vars + .first() + .map(|var| var.value.equivalent(&loc_pattern.value)) + .unwrap_or(false) + { + // This is a case like + // UserId x : [UserId Int] + // UserId x = UserId 42 + // We optimistically parsed the first line as an alias; we now turn it + // into an annotation. + + let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region); + + let value_def = join_alias_to_body( + arena, + header, + ann_type, + spaces_middle, + loc_pattern, + loc_def_expr, + ); + + defs.push_value_def( + value_def, + Region::span_across(&header.name.region, ®ion), + sp_stmt.before, + &[], + ); + + i += 1; + } else { + defs.push_type_def(td, sp_stmt.item.region, sp_stmt.before, &[]) + } + } else { + defs.push_type_def(td, sp_stmt.item.region, sp_stmt.before, &[]) + } + } + Stmt::ValueDef(vd) => { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + // NOTE: it shouldn't be necessary to convert ValueDef::Dbg into an expr, but + // it turns out that ValueDef::Dbg exposes some bugs in the rest of the compiler. + // In particular, it seems that the solver thinks the dbg expr must be a bool. + if let ValueDef::Dbg { + condition, + preceding_comment: _, + } = vd + { + if exprify_dbg { + if i + 1 >= stmts.len() { + return Err(EExpr::DbgContinue(sp_stmt.item.region.end())); + } + + let rest = stmts_to_expr(&stmts[i + 1..], arena)?; + + let e = Expr::Dbg(arena.alloc(condition), arena.alloc(rest)); + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + last_expr = Some(Loc::at(sp_stmt.item.region, e)); + + // don't re-process the rest of the statements; they got consumed by the dbg expr + break; + } + } + + if let ( + ValueDef::Annotation(ann_pattern, ann_type), + Some(( + spaces_middle, + Stmt::ValueDef(ValueDef::Body(loc_pattern, loc_def_expr)), + )), + ) = (vd, stmts.get(i + 1).map(|s| (s.before, s.item.value))) + { + if spaces_middle.len() <= 1 || ann_pattern.value.equivalent(&loc_pattern.value) + { + let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region); + + let value_def = ValueDef::AnnotatedBody { + ann_pattern: arena.alloc(ann_pattern), + ann_type: arena.alloc(ann_type), + comment: spaces_middle + .first() // TODO: Why do we drop all but the first comment???? + .and_then(crate::ast::CommentOrNewline::comment_str), + body_pattern: loc_pattern, + body_expr: loc_def_expr, + }; + + defs.push_value_def( + value_def, + roc_region::all::Region::span_across(&ann_pattern.region, ®ion), + sp_stmt.before, + &[], + ); + i += 1; + } else { + defs.push_value_def(vd, sp_stmt.item.region, sp_stmt.before, &[]) + } + } else { + defs.push_value_def(vd, sp_stmt.item.region, sp_stmt.before, &[]) + } + } + } + + i += 1; + } + Ok((defs, last_expr)) +} + +pub fn join_alias_to_body<'a>( + arena: &'a Bump, + header: TypeHeader<'a>, + ann_type: Loc>, + spaces_middle: &'a [CommentOrNewline<'a>], + body_pattern: &'a Loc>, + body_expr: &'a Loc>, +) -> ValueDef<'a> { + let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x))); + let ann_pattern = Pattern::Apply(loc_name, header.vars); + + let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region)); + let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region); + let loc_ann_pattern = Loc::at(region_ann_pattern, ann_pattern); + + ValueDef::AnnotatedBody { + ann_pattern: arena.alloc(loc_ann_pattern), + ann_type: arena.alloc(ann_type), + comment: spaces_middle + .first() // TODO: Why do we drop all but the first comment???? + .and_then(crate::ast::CommentOrNewline::comment_str), + body_pattern, + body_expr, + } +} + /// This is a helper function for parsing function args. /// The rules for (-) are special-cased, and they come up in function args. /// @@ -2973,7 +3182,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< /// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else. fn assign_or_destructure_identifier<'a>() -> impl Parser<'a, Ident<'a>, EExpr<'a>> { - crate::ident::parse_ident + parse_ident } #[allow(dead_code)] @@ -3242,6 +3451,11 @@ fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>> ) } +enum RecordHelpPrefix { + Update, + Mapper, +} + fn record_prefix_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> { specialize_err( |_, pos| ERecord::Prefix(pos), @@ -3249,11 +3463,6 @@ fn record_prefix_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> { ) } -enum RecordHelpPrefix { - Update, - Mapper, -} - struct RecordHelp<'a> { prefix: Option<(Loc>, RecordHelpPrefix)>, fields: Collection<'a, Loc>>, @@ -3266,13 +3475,11 @@ fn record_help<'a>() -> impl Parser<'a, RecordHelp<'a>, ERecord<'a>> { // You can optionally have an identifier followed by an '&' to // make this a record update, e.g. { Foo.user & username: "blah" }. prefix: optional(backtrackable(and( - spaces_around( - // We wrap the ident in an Expr here, - // so that we have a Spaceable value to work with, - // and then in canonicalization verify that it's an Expr::Var - // (and not e.g. an `Expr::Access`) and extract its string. - loc(record_prefix_identifier()), - ), + // We wrap the ident in an Expr here, + // so that we have a Spaceable value to work with, + // and then in canonicalization verify that it's an Expr::Var + // (and not e.g. an `Expr::Access`) and extract its string. + spaces_around(loc(record_prefix_identifier())), map_with_arena( either( byte(b'&', ERecord::Ampersand), @@ -3489,8 +3696,39 @@ const BINOP_CHAR_MASK: [bool; 125] = { result }; -fn operator<'a>() -> impl Parser<'a, BinOp, EExpr<'a>> { - |_, state, _m| operator_help(EExpr::Start, EExpr::BadOperator, state) +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum OperatorOrDef { + BinOp(BinOp), + Assignment, + AliasOrOpaque(AliasOrOpaque), + Backpassing, +} + +fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> { + move |_, state: State<'a>, _m| { + let start = state.pos(); + let (_, op, state) = operator_help(EExpr::Start, EExpr::BadOperator, state)?; + let err_progress = if check_for_defs { + MadeProgress + } else { + NoProgress + }; + match op { + OperatorOrDef::BinOp(op) => Ok((MadeProgress, op, state)), + OperatorOrDef::Assignment => Err((err_progress, EExpr::BadOperator("=", start))), + OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Alias) => { + Err((err_progress, EExpr::BadOperator(":", start))) + } + OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Opaque) => { + Err((err_progress, EExpr::BadOperator(":=", start))) + } + OperatorOrDef::Backpassing => Err((err_progress, EExpr::BadOperator("<-", start))), + } + } +} + +fn operator<'a>() -> impl Parser<'a, OperatorOrDef, EExpr<'a>> { + (move |_, state, _m| operator_help(EExpr::Start, EExpr::BadOperator, state)).trace("operator") } #[inline(always)] @@ -3498,7 +3736,7 @@ fn operator_help<'a, F, G, E>( to_expectation: F, to_error: G, mut state: State<'a>, -) -> ParseResult<'a, BinOp, E> +) -> ParseResult<'a, OperatorOrDef, E> where F: Fn(Position) -> E, G: Fn(&'a str, Position) -> E, @@ -3522,34 +3760,34 @@ where match chomped { "" => Err((NoProgress, to_expectation(state.pos()))), - "+" => good!(BinOp::Plus, 1), - "-" => good!(BinOp::Minus, 1), - "*" => good!(BinOp::Star, 1), - "/" => good!(BinOp::Slash, 1), - "%" => good!(BinOp::Percent, 1), - "^" => good!(BinOp::Caret, 1), - ">" => good!(BinOp::GreaterThan, 1), - "<" => good!(BinOp::LessThan, 1), + "+" => good!(OperatorOrDef::BinOp(BinOp::Plus), 1), + "-" => good!(OperatorOrDef::BinOp(BinOp::Minus), 1), + "*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1), + "/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1), + "%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1), + "^" => good!(OperatorOrDef::BinOp(BinOp::Caret), 1), + ">" => good!(OperatorOrDef::BinOp(BinOp::GreaterThan), 1), + "<" => good!(OperatorOrDef::BinOp(BinOp::LessThan), 1), "." => { // a `.` makes no progress, so it does not interfere with `.foo` access(or) Err((NoProgress, to_error(".", state.pos()))) } - "=" => good!(BinOp::Assignment, 1), - ":=" => good!(BinOp::IsOpaqueType, 2), - ":" => good!(BinOp::IsAliasType, 1), - "|>" => good!(BinOp::Pizza, 2), - "==" => good!(BinOp::Equals, 2), - "!=" => good!(BinOp::NotEquals, 2), - ">=" => good!(BinOp::GreaterThanOrEq, 2), - "<=" => good!(BinOp::LessThanOrEq, 2), - "&&" => good!(BinOp::And, 2), - "||" => good!(BinOp::Or, 2), - "//" => good!(BinOp::DoubleSlash, 2), + "=" => good!(OperatorOrDef::Assignment, 1), + ":=" => good!(OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Opaque), 2), + ":" => good!(OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Alias), 1), + "|>" => good!(OperatorOrDef::BinOp(BinOp::Pizza), 2), + "==" => good!(OperatorOrDef::BinOp(BinOp::Equals), 2), + "!=" => good!(OperatorOrDef::BinOp(BinOp::NotEquals), 2), + ">=" => good!(OperatorOrDef::BinOp(BinOp::GreaterThanOrEq), 2), + "<=" => good!(OperatorOrDef::BinOp(BinOp::LessThanOrEq), 2), + "&&" => good!(OperatorOrDef::BinOp(BinOp::And), 2), + "||" => good!(OperatorOrDef::BinOp(BinOp::Or), 2), + "//" => good!(OperatorOrDef::BinOp(BinOp::DoubleSlash), 2), "->" => { // makes no progress, so it does not interfere with `_ if isGood -> ...` Err((NoProgress, to_error("->", state.pos()))) } - "<-" => good!(BinOp::Backpassing, 2), + "<-" => good!(OperatorOrDef::Backpassing, 2), "!" => Err((NoProgress, to_error("!", state.pos()))), _ => bad_made_progress!(chomped), } diff --git a/crates/compiler/parse/src/lib.rs b/crates/compiler/parse/src/lib.rs index 007f933efff..dc8a9a3aaa0 100644 --- a/crates/compiler/parse/src/lib.rs +++ b/crates/compiler/parse/src/lib.rs @@ -17,6 +17,7 @@ pub mod module; pub mod number_literal; pub mod pattern; pub mod problems; +pub mod remove_spaces; pub mod src64; pub mod state; pub mod string_literal; diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index 57ba95dbd28..7eec1b83e9a 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -330,6 +330,7 @@ pub enum EExpr<'a> { Start(Position), End(Position), BadExprEnd(Position), + StmtAfterExpr(Position), Space(BadInputError, Position), Dot(Position), @@ -355,6 +356,8 @@ pub enum EExpr<'a> { QualifiedTag(Position), BackpassComma(Position), BackpassArrow(Position), + BackpassContinue(Position), + DbgContinue(Position), When(EWhen<'a>, Position), If(EIf<'a>, Position), @@ -383,6 +386,7 @@ pub enum EExpr<'a> { IndentEnd(Position), UnexpectedComma(Position), + UnexpectedTopLevelExpr(Position), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -851,8 +855,9 @@ where let cur_indent = INDENT.with(|i| *i.borrow()); println!( - "{:<5?}: {}{:<50}", + "{:<5?}:{:<2} {}{:<50}", state.pos(), + min_indent, &indent_text[..cur_indent * 2], self.message ); @@ -868,8 +873,9 @@ where }; println!( - "{:<5?}: {}{:<50} {:<15} {:?}", + "{:<5?}:{:<2} {}{:<50} {:<15} {:?}", state.pos(), + min_indent, &indent_text[..cur_indent * 2], self.message, format!("{:?}", progress), diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs new file mode 100644 index 00000000000..96705783232 --- /dev/null +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -0,0 +1,1749 @@ +use bumpalo::collections::Vec; +use bumpalo::Bump; +use roc_module::called_via::{BinOp, UnaryOp}; +use roc_region::all::{Loc, Position, Region}; + +use crate::{ + ast::{ + AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, Header, Implements, + ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, ImportAsKeyword, + ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, + Module, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, PatternAs, + Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, + WhenBranch, + }, + header::{ + AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, + ImportsKeyword, KeywordItem, ModuleHeader, ModuleName, ModuleParams, PackageEntry, + PackageHeader, PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, + PlatformKeyword, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, + ToKeyword, TypedIdent, WithKeyword, + }, + ident::{BadIdent, UppercaseIdent}, + parser::{ + EAbility, EClosure, EExpect, EExposes, EExpr, EGenerates, EGeneratesWith, EHeader, EIf, + EImport, EImportParams, EImports, EInParens, EList, EPackageEntry, EPackageName, EPackages, + EParams, EPattern, EProvides, ERecord, ERequires, EString, EType, ETypeAbilityImpl, + ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ETypedIdent, + EWhen, PInParens, PList, PRecord, SyntaxError, + }, +}; + +/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting. +/// +/// Currently this consists of: +/// * Removing newlines +/// * Removing comments +/// * Removing parens in Exprs +/// +/// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting) +/// - but there are currently several bugs where they're _not_ preserved. +/// TODO: ensure formatting retains comments +pub trait RemoveSpaces<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self; +} + +macro_rules! keywords { + ($($name:ident),* $(,)?) => { + $( + impl<'a> RemoveSpaces<'a> for $name { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } + } + )* + } +} + +keywords! { + ExposesKeyword, + ImportsKeyword, + WithKeyword, + GeneratesKeyword, + PackageKeyword, + PackagesKeyword, + RequiresKeyword, + ProvidesKeyword, + ToKeyword, + PlatformKeyword, +} + +impl<'a> RemoveSpaces<'a> for Defs<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let mut defs = self.clone(); + + defs.spaces.clear(); + defs.space_before.clear(); + defs.space_after.clear(); + + for type_def in defs.type_defs.iter_mut() { + *type_def = type_def.remove_spaces(arena); + } + + for value_def in defs.value_defs.iter_mut() { + *value_def = value_def.remove_spaces(arena); + } + + for region_def in defs.regions.iter_mut() { + *region_def = region_def.remove_spaces(arena); + } + + defs + } +} + +impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + Spaces { + before: &[], + item: self.item.remove_spaces(arena), + after: &[], + } + } +} + +impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + KeywordItem { + keyword: self.keyword.remove_spaces(arena), + item: self.item.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ProvidesTo { + provides_keyword: self.provides_keyword.remove_spaces(arena), + entries: self.entries.remove_spaces(arena), + types: self.types.remove_spaces(arena), + to_keyword: self.to_keyword.remove_spaces(arena), + to: self.to.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Module<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let header = match &self.header { + Header::Module(header) => Header::Module(ModuleHeader { + after_keyword: &[], + params: header.params.remove_spaces(arena), + exposes: header.exposes.remove_spaces(arena), + interface_imports: header.interface_imports.remove_spaces(arena), + }), + Header::App(header) => Header::App(AppHeader { + before_provides: &[], + provides: header.provides.remove_spaces(arena), + before_packages: &[], + packages: header.packages.remove_spaces(arena), + old_imports: header.old_imports.remove_spaces(arena), + old_provides_to_new_package: header + .old_provides_to_new_package + .remove_spaces(arena), + }), + Header::Package(header) => Header::Package(PackageHeader { + before_exposes: &[], + exposes: header.exposes.remove_spaces(arena), + before_packages: &[], + packages: header.packages.remove_spaces(arena), + }), + Header::Platform(header) => Header::Platform(PlatformHeader { + before_name: &[], + name: header.name.remove_spaces(arena), + requires: header.requires.remove_spaces(arena), + exposes: header.exposes.remove_spaces(arena), + packages: header.packages.remove_spaces(arena), + imports: header.imports.remove_spaces(arena), + provides: header.provides.remove_spaces(arena), + }), + Header::Hosted(header) => Header::Hosted(HostedHeader { + before_name: &[], + name: header.name.remove_spaces(arena), + exposes: header.exposes.remove_spaces(arena), + imports: header.imports.remove_spaces(arena), + generates: header.generates.remove_spaces(arena), + generates_with: header.generates_with.remove_spaces(arena), + }), + }; + Module { + comments: &[], + header, + } + } +} + +impl<'a> RemoveSpaces<'a> for ModuleParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ModuleParams { + params: self.params.remove_spaces(arena), + before_arrow: &[], + after_arrow: &[], + } + } +} + +impl<'a> RemoveSpaces<'a> for Region { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + Region::zero() + } +} + +impl<'a> RemoveSpaces<'a> for &'a str { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + self + } +} + +impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)), + Spaced::SpaceBefore(a, _) => a.remove_spaces(arena), + Spaced::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ExposedName<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for ModuleName<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for PackageName<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for To<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + To::ExistingPackage(a) => To::ExistingPackage(a), + To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)), + } + } +} + +impl<'a> RemoveSpaces<'a> for TypedIdent<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + TypedIdent { + ident: self.ident.remove_spaces(arena), + spaces_before_colon: &[], + ann: self.ann.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + PlatformRequires { + rigids: self.rigids.remove_spaces(arena), + signature: self.signature.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for PackageEntry<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + PackageEntry { + shorthand: self.shorthand, + spaces_after_shorthand: &[], + platform_marker: match self.platform_marker { + Some(_) => Some(&[]), + None => None, + }, + package_name: self.package_name.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)), + ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)), + ImportsEntry::IngestedFile(a, b) => { + ImportsEntry::IngestedFile(a, b.remove_spaces(arena)) + } + } + } +} + +impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + self.as_ref().map(|a| a.remove_spaces(arena)) + } +} + +impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let res = self.value.remove_spaces(arena); + Loc::at(Region::zero(), res) + } +} + +impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + (self.0.remove_spaces(arena), self.1.remove_spaces(arena)) + } +} + +impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let mut items = Vec::with_capacity_in(self.items.len(), arena); + for item in self.items { + items.push(item.remove_spaces(arena)); + } + Collection::with_items(items.into_bump_slice()) + } +} + +impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let mut items = Vec::with_capacity_in(self.len(), arena); + for item in *self { + let res = item.remove_spaces(arena); + items.push(res); + } + items.into_bump_slice() + } +} + +impl<'a> RemoveSpaces<'a> for UnaryOp { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for BinOp { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + arena.alloc((*self).remove_spaces(arena)) + } +} + +impl<'a> RemoveSpaces<'a> for TypeDef<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + use TypeDef::*; + + match *self { + Alias { + header: TypeHeader { name, vars }, + ann, + } => Alias { + header: TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + ann: ann.remove_spaces(arena), + }, + Opaque { + header: TypeHeader { name, vars }, + typ, + derived, + } => Opaque { + header: TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + typ: typ.remove_spaces(arena), + derived: derived.remove_spaces(arena), + }, + Ability { + header: TypeHeader { name, vars }, + loc_implements: loc_has, + members, + } => Ability { + header: TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + loc_implements: loc_has.remove_spaces(arena), + members: members.remove_spaces(arena), + }, + } + } +} + +impl<'a> RemoveSpaces<'a> for ValueDef<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + use ValueDef::*; + + match *self { + Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)), + Body(a, b) => Body( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + AnnotatedBody { + ann_pattern, + ann_type, + comment: _, + body_pattern, + body_expr, + } => AnnotatedBody { + ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), + ann_type: arena.alloc(ann_type.remove_spaces(arena)), + comment: None, + body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), + body_expr: arena.alloc(body_expr.remove_spaces(arena)), + }, + Dbg { + condition, + preceding_comment: _, + } => Dbg { + condition: arena.alloc(condition.remove_spaces(arena)), + preceding_comment: Region::zero(), + }, + Expect { + condition, + preceding_comment: _, + } => Expect { + condition: arena.alloc(condition.remove_spaces(arena)), + preceding_comment: Region::zero(), + }, + ExpectFx { + condition, + preceding_comment: _, + } => ExpectFx { + condition: arena.alloc(condition.remove_spaces(arena)), + preceding_comment: Region::zero(), + }, + ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)), + IngestedFileImport(ingested_file_import) => { + IngestedFileImport(ingested_file_import.remove_spaces(arena)) + } + Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))), + } + } +} + +impl<'a> RemoveSpaces<'a> for ModuleImport<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ModuleImport { + before_name: &[], + name: self.name.remove_spaces(arena), + params: self.params.remove_spaces(arena), + alias: self.alias.remove_spaces(arena), + exposed: self.exposed.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ModuleImportParams { + before: &[], + params: self.params.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + IngestedFileImport { + before_path: &[], + path: self.path.remove_spaces(arena), + name: self.name.remove_spaces(arena), + annotation: self.annotation.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ImportedModuleName { + package: self.package.remove_spaces(arena), + name: self.name.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImportAlias<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for ImportAsKeyword { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for ImportExposingKeyword { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + IngestedFileAnnotation { + before_colon: &[], + annotation: self.annotation.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Implements<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + Implements::Implements + } +} + +impl<'a> RemoveSpaces<'a> for AbilityMember<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + AbilityMember { + name: self.name.remove_spaces(arena), + typ: self.typ.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for WhenBranch<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + WhenBranch { + patterns: self.patterns.remove_spaces(arena), + value: self.value.remove_spaces(arena), + guard: self.guard.remove_spaces(arena), + } + } +} + +impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue( + a.remove_spaces(arena), + arena.alloc([]), + arena.alloc(c.remove_spaces(arena)), + ), + AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue( + a.remove_spaces(arena), + arena.alloc([]), + arena.alloc(c.remove_spaces(arena)), + ), + AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)), + AssignedField::Malformed(a) => AssignedField::Malformed(a), + AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena), + AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value( + a.remove_spaces(arena), + &[], + arena.alloc(c.remove_spaces(arena)), + ), + OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue( + a.remove_spaces(arena), + &[], + &[], + arena.alloc(c.remove_spaces(arena)), + ), + OldRecordBuilderField::LabelOnly(a) => { + OldRecordBuilderField::LabelOnly(a.remove_spaces(arena)) + } + OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a), + OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena), + OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t), + StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)), + StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)), + } + } +} + +impl<'a> RemoveSpaces<'a> for StrSegment<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + StrSegment::Plaintext(t) => StrSegment::Plaintext(t), + StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)), + StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c), + StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)), + StrSegment::DeprecatedInterpolated(t) => { + StrSegment::DeprecatedInterpolated(t.remove_spaces(arena)) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for Expr<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Expr::Float(a) => Expr::Float(a), + Expr::Num(a) => Expr::Num(a), + Expr::NonBase10Int { + string, + base, + is_negative, + } => Expr::NonBase10Int { + string, + base, + is_negative, + }, + Expr::Str(a) => Expr::Str(a.remove_spaces(arena)), + Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b), + Expr::AccessorFunction(a) => Expr::AccessorFunction(a), + Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b), + Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))), + Expr::List(a) => Expr::List(a.remove_spaces(arena)), + Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { + update: arena.alloc(update.remove_spaces(arena)), + fields: fields.remove_spaces(arena), + }, + Expr::Record(a) => Expr::Record(a.remove_spaces(arena)), + Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)), + Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder { + mapper: arena.alloc(mapper.remove_spaces(arena)), + fields: fields.remove_spaces(arena), + }, + Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)), + Expr::Var { module_name, ident } => Expr::Var { module_name, ident }, + Expr::Underscore(a) => Expr::Underscore(a), + Expr::Tag(a) => Expr::Tag(a), + Expr::OpaqueRef(a) => Expr::OpaqueRef(a), + Expr::Closure(a, b) => Expr::Closure( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::Crash => Expr::Crash, + Expr::Defs(a, b) => { + let mut defs = a.clone(); + defs.space_before = vec![Default::default(); defs.len()]; + defs.space_after = vec![Default::default(); defs.len()]; + defs.regions = vec![Region::zero(); defs.len()]; + defs.spaces.clear(); + + for type_def in defs.type_defs.iter_mut() { + *type_def = type_def.remove_spaces(arena); + } + + for value_def in defs.value_defs.iter_mut() { + *value_def = value_def.remove_spaces(arena); + } + + Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena))) + } + Expr::Backpassing(a, b, c) => Expr::Backpassing( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + arena.alloc(c.remove_spaces(arena)), + ), + Expr::Expect(a, b) => Expr::Expect( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::Dbg(a, b) => Expr::Dbg( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::LowLevelDbg(x, a, b) => Expr::LowLevelDbg( + x, + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::Apply(a, b, c) => Expr::Apply( + arena.alloc(a.remove_spaces(arena)), + b.remove_spaces(arena), + c, + ), + Expr::BinOps(a, b) => { + Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))) + } + Expr::UnaryOp(a, b) => { + Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) + } + Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))), + Expr::When(a, b) => { + Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) + } + Expr::ParensAround(a) => { + // The formatter can remove redundant parentheses, so also remove these when normalizing for comparison. + a.remove_spaces(arena) + } + Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), + Expr::MalformedClosure => Expr::MalformedClosure, + Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), + Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), + Expr::SpaceBefore(a, _) => a.remove_spaces(arena), + Expr::SpaceAfter(a, _) => a.remove_spaces(arena), + Expr::SingleQuote(a) => Expr::Num(a), + Expr::MultipleOldRecordBuilders(a) => { + Expr::MultipleOldRecordBuilders(arena.alloc(a.remove_spaces(arena))) + } + Expr::UnappliedOldRecordBuilder(a) => { + Expr::UnappliedOldRecordBuilder(arena.alloc(a.remove_spaces(arena))) + } + Expr::EmptyRecordBuilder(a) => { + Expr::EmptyRecordBuilder(arena.alloc(a.remove_spaces(arena))) + } + Expr::SingleFieldRecordBuilder(a) => { + Expr::SingleFieldRecordBuilder(arena.alloc(a.remove_spaces(arena))) + } + Expr::OptionalFieldInRecordBuilder(a, b) => Expr::OptionalFieldInRecordBuilder( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + } + } +} + +fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent { + match ident { + BadIdent::Start(_) => BadIdent::Start(Position::zero()), + BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()), + BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()), + BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()), + BadIdent::UnderscoreAtStart { + position: _, + declaration_region, + } => BadIdent::UnderscoreAtStart { + position: Position::zero(), + declaration_region, + }, + BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()), + BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()), + BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()), + BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()), + BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()), + BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()), + BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()), + } +} + +impl<'a> RemoveSpaces<'a> for Pattern<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Pattern::Identifier { ident } => Pattern::Identifier { ident }, + Pattern::Tag(a) => Pattern::Tag(a), + Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), + Pattern::Apply(a, b) => Pattern::Apply( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)), + Pattern::RequiredField(a, b) => { + Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena))) + } + Pattern::OptionalField(a, b) => { + Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena))) + } + Pattern::As(pattern, pattern_as) => Pattern::As( + arena.alloc(pattern.remove_spaces(arena)), + pattern_as.remove_spaces(arena), + ), + Pattern::NumLiteral(a) => Pattern::NumLiteral(a), + Pattern::NonBase10Literal { + string, + base, + is_negative, + } => Pattern::NonBase10Literal { + string, + base, + is_negative, + }, + Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a), + Pattern::StrLiteral(a) => Pattern::StrLiteral(a), + Pattern::Underscore(a) => Pattern::Underscore(a), + Pattern::Malformed(a) => Pattern::Malformed(a), + Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)), + Pattern::QualifiedIdentifier { module_name, ident } => { + Pattern::QualifiedIdentifier { module_name, ident } + } + Pattern::SpaceBefore(a, _) => a.remove_spaces(arena), + Pattern::SpaceAfter(a, _) => a.remove_spaces(arena), + Pattern::SingleQuote(a) => Pattern::SingleQuote(a), + Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)), + Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)), + Pattern::ListRest(opt_pattern_as) => Pattern::ListRest( + opt_pattern_as + .map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))), + ), + } + } +} + +impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + TypeAnnotation::Function(a, b) => TypeAnnotation::Function( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)), + TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a), + TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As( + arena.alloc(a.remove_spaces(arena)), + &[], + TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + ), + TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple { + elems: fields.remove_spaces(arena), + ext: ext.remove_spaces(arena), + }, + TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record { + fields: fields.remove_spaces(arena), + ext: ext.remove_spaces(arena), + }, + TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion { + ext: ext.remove_spaces(arena), + tags: tags.remove_spaces(arena), + }, + TypeAnnotation::Inferred => TypeAnnotation::Inferred, + TypeAnnotation::Wildcard => TypeAnnotation::Wildcard, + TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where( + arena.alloc(annot.remove_spaces(arena)), + arena.alloc(has_clauses.remove_spaces(arena)), + ), + TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena), + TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena), + TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ImplementsClause { + var: self.var.remove_spaces(arena), + abilities: self.abilities.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Tag<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Tag::Apply { name, args } => Tag::Apply { + name: name.remove_spaces(arena), + args: args.remove_spaces(arena), + }, + Tag::Malformed(a) => Tag::Malformed(a), + Tag::SpaceBefore(a, _) => a.remove_spaces(arena), + Tag::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + AbilityImpls::AbilityImpls(impls) => { + AbilityImpls::AbilityImpls(impls.remove_spaces(arena)) + } + AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => { + has.remove_spaces(arena) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + ImplementsAbility::ImplementsAbility { ability, impls } => { + ImplementsAbility::ImplementsAbility { + ability: ability.remove_spaces(arena), + impls: impls.remove_spaces(arena), + } + } + ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => { + has.remove_spaces(arena) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + ImplementsAbilities::Implements(derived) => { + ImplementsAbilities::Implements(derived.remove_spaces(arena)) + } + ImplementsAbilities::SpaceBefore(derived, _) + | ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for PatternAs<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + PatternAs { + spaces_before: &[], + identifier: self.identifier.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for EExpr<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EExpr::TrailingOperator(_pos) => EExpr::TrailingOperator(Position::zero()), + EExpr::Start(_pos) => EExpr::Start(Position::zero()), + EExpr::End(_pos) => EExpr::End(Position::zero()), + EExpr::BadExprEnd(_pos) => EExpr::BadExprEnd(Position::zero()), + EExpr::Space(inner_err, _pos) => EExpr::Space(*inner_err, Position::zero()), + EExpr::Dot(_pos) => EExpr::Dot(Position::zero()), + EExpr::Access(_pos) => EExpr::Access(Position::zero()), + EExpr::UnaryNot(_pos) => EExpr::UnaryNot(Position::zero()), + EExpr::UnaryNegate(_pos) => EExpr::UnaryNegate(Position::zero()), + EExpr::BadOperator(inner_err, _pos) => EExpr::BadOperator( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpr::DefMissingFinalExpr(_pos) => EExpr::DefMissingFinalExpr(Position::zero()), + EExpr::DefMissingFinalExpr2(inner_err, _pos) => EExpr::DefMissingFinalExpr2( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpr::Type(inner_err, _pos) => { + EExpr::Type(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Pattern(inner_err, _pos) => EExpr::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpr::Ability(inner_err, _pos) => { + EExpr::Ability(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::IndentDefBody(_pos) => EExpr::IndentDefBody(Position::zero()), + EExpr::IndentEquals(_pos) => EExpr::IndentEquals(Position::zero()), + EExpr::IndentAnnotation(_pos) => EExpr::IndentAnnotation(Position::zero()), + EExpr::Equals(_pos) => EExpr::Equals(Position::zero()), + EExpr::Colon(_pos) => EExpr::Colon(Position::zero()), + EExpr::DoubleColon(_pos) => EExpr::DoubleColon(Position::zero()), + EExpr::Ident(_pos) => EExpr::Ident(Position::zero()), + EExpr::ElmStyleFunction(_region, _pos) => { + EExpr::ElmStyleFunction(Region::zero(), Position::zero()) + } + EExpr::MalformedPattern(_pos) => EExpr::MalformedPattern(Position::zero()), + EExpr::QualifiedTag(_pos) => EExpr::QualifiedTag(Position::zero()), + EExpr::BackpassComma(_pos) => EExpr::BackpassComma(Position::zero()), + EExpr::BackpassArrow(_pos) => EExpr::BackpassArrow(Position::zero()), + EExpr::BackpassContinue(_pos) => EExpr::BackpassContinue(Position::zero()), + EExpr::DbgContinue(_pos) => EExpr::DbgContinue(Position::zero()), + EExpr::When(inner_err, _pos) => { + EExpr::When(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::If(inner_err, _pos) => { + EExpr::If(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Expect(inner_err, _pos) => { + EExpr::Expect(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Dbg(inner_err, _pos) => { + EExpr::Dbg(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Import(inner_err, _pos) => { + EExpr::Import(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Closure(inner_err, _pos) => { + EExpr::Closure(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Underscore(_pos) => EExpr::Underscore(Position::zero()), + EExpr::Crash(_pos) => EExpr::Crash(Position::zero()), + EExpr::InParens(inner_err, _pos) => { + EExpr::InParens(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Record(inner_err, _pos) => { + EExpr::Record(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::OptionalValueInRecordBuilder(_pos) => { + EExpr::OptionalValueInRecordBuilder(Region::zero()) + } + EExpr::Str(inner_err, _pos) => { + EExpr::Str(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Number(inner_err, _pos) => EExpr::Number(inner_err.clone(), Position::zero()), + EExpr::List(inner_err, _pos) => { + EExpr::List(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::IndentStart(_pos) => EExpr::IndentStart(Position::zero()), + EExpr::IndentEnd(_pos) => EExpr::IndentEnd(Position::zero()), + EExpr::UnexpectedComma(_pos) => EExpr::UnexpectedComma(Position::zero()), + EExpr::UnexpectedTopLevelExpr(_pos) => EExpr::UnexpectedTopLevelExpr(Position::zero()), + EExpr::StmtAfterExpr(_pos) => EExpr::StmtAfterExpr(Position::zero()), + EExpr::RecordUpdateAccumulator(_) => EExpr::RecordUpdateAccumulator(Region::zero()), + EExpr::RecordBuilderAccumulator(_) => EExpr::RecordBuilderAccumulator(Region::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EList<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EList::Open(_pos) => EList::Open(Position::zero()), + EList::End(_pos) => EList::End(Position::zero()), + EList::Space(inner_err, _pos) => EList::Space(*inner_err, Position::zero()), + EList::Expr(inner_err, _pos) => EList::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + } + } +} + +impl<'a> RemoveSpaces<'a> for EString<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EString::Open(_) => EString::Open(Position::zero()), + EString::CodePtOpen(_) => EString::CodePtOpen(Position::zero()), + EString::CodePtEnd(_) => EString::CodePtEnd(Position::zero()), + EString::InvalidSingleQuote(inner, _) => { + EString::InvalidSingleQuote(*inner, Position::zero()) + } + EString::Space(inner, _) => EString::Space(*inner, Position::zero()), + EString::EndlessSingleLine(_) => EString::EndlessSingleLine(Position::zero()), + EString::EndlessMultiLine(_) => EString::EndlessMultiLine(Position::zero()), + EString::EndlessSingleQuote(_) => EString::EndlessSingleQuote(Position::zero()), + EString::UnknownEscape(_) => EString::UnknownEscape(Position::zero()), + EString::Format(inner, _) => { + EString::Format(arena.alloc(inner.remove_spaces(arena)), Position::zero()) + } + EString::FormatEnd(_) => EString::FormatEnd(Position::zero()), + EString::MultilineInsufficientIndent(_) => { + EString::MultilineInsufficientIndent(Position::zero()) + } + EString::ExpectedDoubleQuoteGotSingleQuote(_) => { + EString::ExpectedDoubleQuoteGotSingleQuote(Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EClosure<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EClosure::Space(inner_err, _) => EClosure::Space(*inner_err, Position::zero()), + EClosure::Start(_) => EClosure::Start(Position::zero()), + EClosure::Arrow(_) => EClosure::Arrow(Position::zero()), + EClosure::Comma(_) => EClosure::Comma(Position::zero()), + EClosure::Arg(_) => EClosure::Arg(Position::zero()), + EClosure::Pattern(inner_err, _) => { + EClosure::Pattern(inner_err.remove_spaces(arena), Position::zero()) + } + EClosure::Body(inner_err, _) => EClosure::Body( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()), + EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()), + EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EInParens<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EInParens::End(_) => EInParens::End(Position::zero()), + EInParens::Open(_) => EInParens::Open(Position::zero()), + EInParens::Empty(_) => EInParens::Empty(Position::zero()), + EInParens::Expr(inner_err, _) => EInParens::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EInParens::Space(inner_err, _) => EInParens::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ERecord<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ERecord::End(_) => ERecord::End(Position::zero()), + ERecord::Open(_) => ERecord::Open(Position::zero()), + ERecord::Field(_pos) => ERecord::Field(Position::zero()), + ERecord::Colon(_) => ERecord::Colon(Position::zero()), + ERecord::QuestionMark(_) => ERecord::QuestionMark(Position::zero()), + ERecord::Arrow(_) => ERecord::Arrow(Position::zero()), + ERecord::Ampersand(_) => ERecord::Ampersand(Position::zero()), + ERecord::Expr(inner_err, _) => ERecord::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ERecord::Space(inner_err, _) => ERecord::Space(*inner_err, Position::zero()), + ERecord::Prefix(_) => ERecord::Prefix(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPattern<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPattern::Record(inner_err, _) => { + EPattern::Record(inner_err.remove_spaces(arena), Position::zero()) + } + EPattern::List(inner_err, _) => { + EPattern::List(inner_err.remove_spaces(arena), Position::zero()) + } + EPattern::AsKeyword(_) => EPattern::AsKeyword(Position::zero()), + EPattern::AsIdentifier(_) => EPattern::AsIdentifier(Position::zero()), + EPattern::Underscore(_) => EPattern::Underscore(Position::zero()), + EPattern::NotAPattern(_) => EPattern::NotAPattern(Position::zero()), + EPattern::Start(_) => EPattern::Start(Position::zero()), + EPattern::End(_) => EPattern::End(Position::zero()), + EPattern::Space(inner_err, _) => EPattern::Space(*inner_err, Position::zero()), + EPattern::PInParens(inner_err, _) => { + EPattern::PInParens(inner_err.remove_spaces(arena), Position::zero()) + } + EPattern::NumLiteral(inner_err, _) => { + EPattern::NumLiteral(inner_err.clone(), Position::zero()) + } + EPattern::IndentStart(_) => EPattern::IndentStart(Position::zero()), + EPattern::IndentEnd(_) => EPattern::IndentEnd(Position::zero()), + EPattern::AsIndentStart(_) => EPattern::AsIndentStart(Position::zero()), + EPattern::AccessorFunction(_) => EPattern::AccessorFunction(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EImport<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EImport::Import(_) => EImport::Import(Position::zero()), + EImport::IndentStart(_) => EImport::IndentStart(Position::zero()), + EImport::PackageShorthand(_) => EImport::PackageShorthand(Position::zero()), + EImport::PackageShorthandDot(_) => EImport::PackageShorthandDot(Position::zero()), + EImport::ModuleName(_) => EImport::ModuleName(Position::zero()), + EImport::Params(inner_err, _) => { + EImport::Params(inner_err.remove_spaces(arena), Position::zero()) + } + EImport::IndentAs(_) => EImport::IndentAs(Position::zero()), + EImport::As(_) => EImport::As(Position::zero()), + EImport::IndentAlias(_) => EImport::IndentAlias(Position::zero()), + EImport::Alias(_) => EImport::Alias(Position::zero()), + EImport::LowercaseAlias(_) => EImport::LowercaseAlias(Region::zero()), + EImport::IndentExposing(_) => EImport::IndentExposing(Position::zero()), + EImport::Exposing(_) => EImport::Exposing(Position::zero()), + EImport::ExposingListStart(_) => EImport::ExposingListStart(Position::zero()), + EImport::ExposedName(_) => EImport::ExposedName(Position::zero()), + EImport::ExposingListEnd(_) => EImport::ExposingListEnd(Position::zero()), + EImport::IndentIngestedPath(_) => EImport::IndentIngestedPath(Position::zero()), + EImport::IngestedPath(_) => EImport::IngestedPath(Position::zero()), + EImport::IndentIngestedName(_) => EImport::IndentIngestedName(Position::zero()), + EImport::IngestedName(_) => EImport::IngestedName(Position::zero()), + EImport::IndentColon(_) => EImport::IndentColon(Position::zero()), + EImport::Colon(_) => EImport::Colon(Position::zero()), + EImport::IndentAnnotation(_) => EImport::IndentAnnotation(Position::zero()), + EImport::Annotation(inner_err, _) => { + EImport::Annotation(inner_err.remove_spaces(arena), Position::zero()) + } + EImport::Space(inner_err, _) => EImport::Space(*inner_err, Position::zero()), + EImport::EndNewline(_) => EImport::EndNewline(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EType<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EType::Space(inner_err, _) => EType::Space(*inner_err, Position::zero()), + EType::UnderscoreSpacing(_) => EType::UnderscoreSpacing(Position::zero()), + EType::TRecord(inner_err, _) => { + EType::TRecord(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TTagUnion(inner_err, _) => { + EType::TTagUnion(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TInParens(inner_err, _) => { + EType::TInParens(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TApply(inner_err, _) => { + EType::TApply(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TInlineAlias(inner_err, _) => { + EType::TInlineAlias(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TBadTypeVariable(_) => EType::TBadTypeVariable(Position::zero()), + EType::TWildcard(_) => EType::TWildcard(Position::zero()), + EType::TInferred(_) => EType::TInferred(Position::zero()), + EType::TStart(_) => EType::TStart(Position::zero()), + EType::TEnd(_) => EType::TEnd(Position::zero()), + EType::TFunctionArgument(_) => EType::TFunctionArgument(Position::zero()), + EType::TWhereBar(_) => EType::TWhereBar(Position::zero()), + EType::TImplementsClause(_) => EType::TImplementsClause(Position::zero()), + EType::TAbilityImpl(inner_err, _) => { + EType::TAbilityImpl(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TIndentStart(_) => EType::TIndentStart(Position::zero()), + EType::TIndentEnd(_) => EType::TIndentEnd(Position::zero()), + EType::TAsIndentStart(_) => EType::TAsIndentStart(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EImportParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EImportParams::Indent(_) => EImportParams::Indent(Position::zero()), + EImportParams::Record(inner_err, _) => { + EImportParams::Record(inner_err.remove_spaces(arena), Position::zero()) + } + EImportParams::RecordUpdateFound(_) => EImportParams::RecordUpdateFound(Region::zero()), + EImportParams::RecordApplyFound(_) => EImportParams::RecordApplyFound(Region::zero()), + EImportParams::Space(inner_err, _) => { + EImportParams::Space(*inner_err, Position::zero()) + } + EImportParams::RecordBuilderFound(_) => { + EImportParams::RecordBuilderFound(Region::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for PInParens<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + PInParens::Empty(_) => PInParens::Empty(Position::zero()), + PInParens::End(_) => PInParens::End(Position::zero()), + PInParens::Open(_) => PInParens::Open(Position::zero()), + PInParens::Pattern(inner_err, _) => PInParens::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PInParens::Space(inner_err, _) => PInParens::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeAbilityImpl<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeAbilityImpl::End(_) => ETypeAbilityImpl::End(Position::zero()), + ETypeAbilityImpl::Open(_) => ETypeAbilityImpl::Open(Position::zero()), + ETypeAbilityImpl::Field(_) => ETypeAbilityImpl::Field(Position::zero()), + ETypeAbilityImpl::Colon(_) => ETypeAbilityImpl::Colon(Position::zero()), + ETypeAbilityImpl::Arrow(_) => ETypeAbilityImpl::Arrow(Position::zero()), + ETypeAbilityImpl::Optional(_) => ETypeAbilityImpl::Optional(Position::zero()), + ETypeAbilityImpl::Type(inner_err, _) => ETypeAbilityImpl::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeAbilityImpl::Space(inner_err, _) => { + ETypeAbilityImpl::Space(*inner_err, Position::zero()) + } + ETypeAbilityImpl::QuestionMark(_) => ETypeAbilityImpl::QuestionMark(Position::zero()), + ETypeAbilityImpl::Ampersand(_) => ETypeAbilityImpl::Ampersand(Position::zero()), + ETypeAbilityImpl::Expr(inner_err, _) => ETypeAbilityImpl::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeAbilityImpl::IndentBar(_) => ETypeAbilityImpl::IndentBar(Position::zero()), + ETypeAbilityImpl::IndentAmpersand(_) => { + ETypeAbilityImpl::IndentAmpersand(Position::zero()) + } + ETypeAbilityImpl::Prefix(_) => ETypeAbilityImpl::Prefix(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeInlineAlias { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + ETypeInlineAlias::NotAnAlias(_pos) => ETypeInlineAlias::NotAnAlias(Position::zero()), + ETypeInlineAlias::Qualified(_pos) => ETypeInlineAlias::Qualified(Position::zero()), + ETypeInlineAlias::ArgumentNotLowercase(_pos) => { + ETypeInlineAlias::ArgumentNotLowercase(Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeApply { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + ETypeApply::StartNotUppercase(_) => ETypeApply::StartNotUppercase(Position::zero()), + ETypeApply::End(_) => ETypeApply::End(Position::zero()), + ETypeApply::Space(inner_err, _) => ETypeApply::Space(*inner_err, Position::zero()), + ETypeApply::DoubleDot(_) => ETypeApply::DoubleDot(Position::zero()), + ETypeApply::TrailingDot(_) => ETypeApply::TrailingDot(Position::zero()), + ETypeApply::StartIsNumber(_) => ETypeApply::StartIsNumber(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeInParens<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeInParens::Empty(_) => ETypeInParens::Empty(Position::zero()), + ETypeInParens::End(_) => ETypeInParens::End(Position::zero()), + ETypeInParens::Open(_) => ETypeInParens::Open(Position::zero()), + ETypeInParens::Type(inner_err, _) => ETypeInParens::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeInParens::Space(inner_err, _) => { + ETypeInParens::Space(*inner_err, Position::zero()) + } + ETypeInParens::IndentOpen(_) => ETypeInParens::IndentOpen(Position::zero()), + ETypeInParens::IndentEnd(_) => ETypeInParens::IndentEnd(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeTagUnion<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeTagUnion::End(_) => ETypeTagUnion::End(Position::zero()), + ETypeTagUnion::Open(_) => ETypeTagUnion::Open(Position::zero()), + ETypeTagUnion::Type(inner_err, _) => ETypeTagUnion::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeTagUnion::Space(inner_err, _) => { + ETypeTagUnion::Space(*inner_err, Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeRecord<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeRecord::End(_) => ETypeRecord::End(Position::zero()), + ETypeRecord::Open(_) => ETypeRecord::Open(Position::zero()), + ETypeRecord::Field(_) => ETypeRecord::Field(Position::zero()), + ETypeRecord::Colon(_) => ETypeRecord::Colon(Position::zero()), + ETypeRecord::Optional(_) => ETypeRecord::Optional(Position::zero()), + ETypeRecord::Type(inner_err, _) => ETypeRecord::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeRecord::Space(inner_err, _) => ETypeRecord::Space(*inner_err, Position::zero()), + ETypeRecord::IndentOpen(_) => ETypeRecord::IndentOpen(Position::zero()), + ETypeRecord::IndentColon(_) => ETypeRecord::IndentColon(Position::zero()), + ETypeRecord::IndentOptional(_) => ETypeRecord::IndentOptional(Position::zero()), + ETypeRecord::IndentEnd(_) => ETypeRecord::IndentEnd(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for PRecord<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + PRecord::End(_) => PRecord::End(Position::zero()), + PRecord::Open(_) => PRecord::Open(Position::zero()), + PRecord::Field(_) => PRecord::Field(Position::zero()), + PRecord::Colon(_) => PRecord::Colon(Position::zero()), + PRecord::Optional(_) => PRecord::Optional(Position::zero()), + PRecord::Pattern(inner_err, _) => PRecord::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PRecord::Expr(inner_err, _) => PRecord::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PRecord::Space(inner_err, _) => PRecord::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for PList<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + PList::End(_) => PList::End(Position::zero()), + PList::Open(_) => PList::Open(Position::zero()), + PList::Rest(_) => PList::Rest(Position::zero()), + PList::Pattern(inner_err, _) => PList::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PList::Space(inner_err, _) => PList::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EExpect<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EExpect::Space(inner_err, _) => EExpect::Space(*inner_err, Position::zero()), + EExpect::Dbg(_) => EExpect::Dbg(Position::zero()), + EExpect::Expect(_) => EExpect::Expect(Position::zero()), + EExpect::Condition(inner_err, _) => EExpect::Condition( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpect::Continuation(inner_err, _) => EExpect::Continuation( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()), + } + } +} +impl<'a> RemoveSpaces<'a> for EIf<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EIf::Space(inner_err, _) => EIf::Space(*inner_err, Position::zero()), + EIf::If(_) => EIf::If(Position::zero()), + EIf::Then(_) => EIf::Then(Position::zero()), + EIf::Else(_) => EIf::Else(Position::zero()), + EIf::Condition(inner_err, _) => EIf::Condition( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EIf::ThenBranch(inner_err, _) => EIf::ThenBranch( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EIf::ElseBranch(inner_err, _) => EIf::ElseBranch( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EIf::IndentCondition(_) => EIf::IndentCondition(Position::zero()), + EIf::IndentIf(_) => EIf::IndentIf(Position::zero()), + EIf::IndentThenToken(_) => EIf::IndentThenToken(Position::zero()), + EIf::IndentElseToken(_) => EIf::IndentElseToken(Position::zero()), + EIf::IndentThenBranch(_) => EIf::IndentThenBranch(Position::zero()), + EIf::IndentElseBranch(_) => EIf::IndentElseBranch(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EWhen<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EWhen::Space(inner_err, _) => EWhen::Space(*inner_err, Position::zero()), + EWhen::When(_) => EWhen::When(Position::zero()), + EWhen::Is(_) => EWhen::Is(Position::zero()), + EWhen::Pattern(inner_err, _) => { + EWhen::Pattern(inner_err.remove_spaces(arena), Position::zero()) + } + EWhen::Arrow(_) => EWhen::Arrow(Position::zero()), + EWhen::Bar(_) => EWhen::Bar(Position::zero()), + EWhen::IfToken(_) => EWhen::IfToken(Position::zero()), + EWhen::IfGuard(inner_err, _) => EWhen::IfGuard( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EWhen::Condition(inner_err, _) => EWhen::Condition( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EWhen::Branch(inner_err, _) => EWhen::Branch( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EWhen::IndentCondition(_) => EWhen::IndentCondition(Position::zero()), + EWhen::IndentPattern(_) => EWhen::IndentPattern(Position::zero()), + EWhen::IndentArrow(_) => EWhen::IndentArrow(Position::zero()), + EWhen::IndentBranch(_) => EWhen::IndentBranch(Position::zero()), + EWhen::IndentIfGuard(_) => EWhen::IndentIfGuard(Position::zero()), + EWhen::PatternAlignment(_alignment, _) => EWhen::PatternAlignment(0, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EAbility<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EAbility::Space(inner_err, _) => EAbility::Space(*inner_err, Position::zero()), + EAbility::Type(inner_err, _) => { + EAbility::Type(inner_err.remove_spaces(arena), Position::zero()) + } + EAbility::DemandAlignment(_alignment, _) => { + EAbility::DemandAlignment(0, Position::zero()) + } + EAbility::DemandName(_) => EAbility::DemandName(Position::zero()), + EAbility::DemandColon(_) => EAbility::DemandColon(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EGeneratesWith { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EGeneratesWith::Open(_) => EGeneratesWith::Open(Position::zero()), + EGeneratesWith::With(_) => EGeneratesWith::With(Position::zero()), + EGeneratesWith::IndentWith(_) => EGeneratesWith::IndentWith(Position::zero()), + EGeneratesWith::IndentListStart(_) => EGeneratesWith::IndentListStart(Position::zero()), + EGeneratesWith::IndentListEnd(_) => EGeneratesWith::IndentListEnd(Position::zero()), + EGeneratesWith::ListStart(_) => EGeneratesWith::ListStart(Position::zero()), + EGeneratesWith::ListEnd(_) => EGeneratesWith::ListEnd(Position::zero()), + EGeneratesWith::Identifier(_) => EGeneratesWith::Identifier(Position::zero()), + EGeneratesWith::Space(inner_err, _) => { + EGeneratesWith::Space(*inner_err, Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EGenerates { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EGenerates::Open(_) => EGenerates::Open(Position::zero()), + EGenerates::Generates(_) => EGenerates::Generates(Position::zero()), + EGenerates::IndentGenerates(_) => EGenerates::IndentGenerates(Position::zero()), + EGenerates::Identifier(_) => EGenerates::Identifier(Position::zero()), + EGenerates::Space(inner_err, _) => EGenerates::Space(*inner_err, Position::zero()), + EGenerates::IndentTypeStart(_) => EGenerates::IndentTypeStart(Position::zero()), + EGenerates::IndentTypeEnd(_) => EGenerates::IndentTypeEnd(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPackages<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPackages::Open(_) => EPackages::Open(Position::zero()), + EPackages::Space(inner_err, _) => EPackages::Space(*inner_err, Position::zero()), + EPackages::Packages(_) => EPackages::Packages(Position::zero()), + EPackages::IndentPackages(_) => EPackages::IndentPackages(Position::zero()), + EPackages::ListStart(_) => EPackages::ListStart(Position::zero()), + EPackages::ListEnd(_) => EPackages::ListEnd(Position::zero()), + EPackages::IndentListStart(_) => EPackages::IndentListStart(Position::zero()), + EPackages::IndentListEnd(_) => EPackages::IndentListEnd(Position::zero()), + EPackages::PackageEntry(inner_err, _) => { + EPackages::PackageEntry(inner_err.remove_spaces(arena), Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EHeader<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EHeader::Provides(inner_err, _) => { + EHeader::Provides(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Params(inner_err, _) => { + EHeader::Params(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Exposes(inner_err, _) => { + EHeader::Exposes(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Imports(inner_err, _) => { + EHeader::Imports(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Requires(inner_err, _) => { + EHeader::Requires(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Packages(inner_err, _) => { + EHeader::Packages(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Generates(inner_err, _) => { + EHeader::Generates(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::GeneratesWith(inner_err, _) => { + EHeader::GeneratesWith(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Space(inner_err, _) => EHeader::Space(*inner_err, Position::zero()), + EHeader::Start(_) => EHeader::Start(Position::zero()), + EHeader::ModuleName(_) => EHeader::ModuleName(Position::zero()), + EHeader::AppName(inner_err, _) => { + EHeader::AppName(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::PackageName(inner_err, _) => { + EHeader::PackageName(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::PlatformName(inner_err, _) => { + EHeader::PlatformName(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::IndentStart(_) => EHeader::IndentStart(Position::zero()), + EHeader::InconsistentModuleName(_) => EHeader::InconsistentModuleName(Region::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPackageName<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPackageName::BadPath(inner_err, _) => { + EPackageName::BadPath(inner_err.remove_spaces(arena), Position::zero()) + } + EPackageName::Escapes(_) => EPackageName::Escapes(Position::zero()), + EPackageName::Multiline(_) => EPackageName::Multiline(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for SyntaxError<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + SyntaxError::Unexpected(_) => SyntaxError::Unexpected(Region::zero()), + SyntaxError::OutdentedTooFar => SyntaxError::OutdentedTooFar, + SyntaxError::Eof(_) => SyntaxError::Eof(Region::zero()), + SyntaxError::InvalidPattern => SyntaxError::InvalidPattern, + SyntaxError::BadUtf8 => SyntaxError::BadUtf8, + SyntaxError::ReservedKeyword(_) => SyntaxError::ReservedKeyword(Region::zero()), + SyntaxError::ArgumentsBeforeEquals(_) => { + SyntaxError::ArgumentsBeforeEquals(Region::zero()) + } + SyntaxError::NotYetImplemented(text) => SyntaxError::NotYetImplemented(text.clone()), + SyntaxError::Todo => SyntaxError::Todo, + SyntaxError::Type(err) => SyntaxError::Type(err.remove_spaces(arena)), + SyntaxError::Pattern(err) => SyntaxError::Pattern(err.remove_spaces(arena)), + SyntaxError::Expr(err, _) => { + SyntaxError::Expr(err.remove_spaces(arena), Position::zero()) + } + SyntaxError::Header(err) => SyntaxError::Header(err.remove_spaces(arena)), + SyntaxError::Space(inner_err) => SyntaxError::Space(*inner_err), + SyntaxError::NotEndOfFile(_) => SyntaxError::NotEndOfFile(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPackageEntry<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPackageEntry::BadPackage(inner_err, _) => { + EPackageEntry::BadPackage(inner_err.remove_spaces(arena), Position::zero()) + } + EPackageEntry::Shorthand(_) => EPackageEntry::Shorthand(Position::zero()), + EPackageEntry::Colon(_) => EPackageEntry::Colon(Position::zero()), + EPackageEntry::IndentPackage(_) => EPackageEntry::IndentPackage(Position::zero()), + EPackageEntry::IndentPlatform(_) => EPackageEntry::IndentPlatform(Position::zero()), + EPackageEntry::Platform(_) => EPackageEntry::Platform(Position::zero()), + EPackageEntry::Space(inner_err, _) => { + EPackageEntry::Space(*inner_err, Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EProvides<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EProvides::Provides(_) => EProvides::Provides(Position::zero()), + EProvides::Open(_) => EProvides::Open(Position::zero()), + EProvides::To(_) => EProvides::To(Position::zero()), + EProvides::IndentProvides(_) => EProvides::IndentProvides(Position::zero()), + EProvides::IndentTo(_) => EProvides::IndentTo(Position::zero()), + EProvides::IndentListStart(_) => EProvides::IndentListStart(Position::zero()), + EProvides::IndentPackage(_) => EProvides::IndentPackage(Position::zero()), + EProvides::ListStart(_) => EProvides::ListStart(Position::zero()), + EProvides::ListEnd(_) => EProvides::ListEnd(Position::zero()), + EProvides::Identifier(_) => EProvides::Identifier(Position::zero()), + EProvides::Package(inner_err, _) => { + EProvides::Package(inner_err.remove_spaces(arena), Position::zero()) + } + EProvides::Space(inner_err, _) => EProvides::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EParams::Pattern(inner_err, _) => { + EParams::Pattern(inner_err.remove_spaces(arena), Position::zero()) + } + EParams::BeforeArrow(_) => EParams::BeforeArrow(Position::zero()), + EParams::Arrow(_) => EParams::Arrow(Position::zero()), + EParams::AfterArrow(_) => EParams::AfterArrow(Position::zero()), + EParams::Space(inner_err, _) => EParams::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EExposes { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EExposes::Exposes(_) => EExposes::Exposes(Position::zero()), + EExposes::Open(_) => EExposes::Open(Position::zero()), + EExposes::IndentExposes(_) => EExposes::IndentExposes(Position::zero()), + EExposes::IndentListStart(_) => EExposes::IndentListStart(Position::zero()), + EExposes::ListStart(_) => EExposes::ListStart(Position::zero()), + EExposes::ListEnd(_) => EExposes::ListEnd(Position::zero()), + EExposes::Identifier(_) => EExposes::Identifier(Position::zero()), + EExposes::Space(inner_err, _) => EExposes::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EImports { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EImports::Open(_) => EImports::Open(Position::zero()), + EImports::Imports(_) => EImports::Imports(Position::zero()), + EImports::IndentImports(_) => EImports::IndentImports(Position::zero()), + EImports::IndentListStart(_) => EImports::IndentListStart(Position::zero()), + EImports::IndentListEnd(_) => EImports::IndentListEnd(Position::zero()), + EImports::ListStart(_) => EImports::ListStart(Position::zero()), + EImports::ListEnd(_) => EImports::ListEnd(Position::zero()), + EImports::Identifier(_) => EImports::Identifier(Position::zero()), + EImports::ExposingDot(_) => EImports::ExposingDot(Position::zero()), + EImports::ShorthandDot(_) => EImports::ShorthandDot(Position::zero()), + EImports::Shorthand(_) => EImports::Shorthand(Position::zero()), + EImports::ModuleName(_) => EImports::ModuleName(Position::zero()), + EImports::Space(inner_err, _) => EImports::Space(*inner_err, Position::zero()), + EImports::IndentSetStart(_) => EImports::IndentSetStart(Position::zero()), + EImports::SetStart(_) => EImports::SetStart(Position::zero()), + EImports::SetEnd(_) => EImports::SetEnd(Position::zero()), + EImports::TypedIdent(_) => EImports::TypedIdent(Position::zero()), + EImports::AsKeyword(_) => EImports::AsKeyword(Position::zero()), + EImports::StrLiteral(_) => EImports::StrLiteral(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ERequires<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ERequires::Requires(_) => ERequires::Requires(Position::zero()), + ERequires::Open(_) => ERequires::Open(Position::zero()), + ERequires::IndentRequires(_) => ERequires::IndentRequires(Position::zero()), + ERequires::IndentListStart(_) => ERequires::IndentListStart(Position::zero()), + ERequires::ListStart(_) => ERequires::ListStart(Position::zero()), + ERequires::ListEnd(_) => ERequires::ListEnd(Position::zero()), + ERequires::TypedIdent(inner_err, _) => { + ERequires::TypedIdent(inner_err.remove_spaces(arena), Position::zero()) + } + ERequires::Rigid(_) => ERequires::Rigid(Position::zero()), + ERequires::Space(inner_err, _) => ERequires::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypedIdent<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypedIdent::Space(inner_err, _) => ETypedIdent::Space(*inner_err, Position::zero()), + ETypedIdent::HasType(_) => ETypedIdent::HasType(Position::zero()), + ETypedIdent::IndentHasType(_) => ETypedIdent::IndentHasType(Position::zero()), + ETypedIdent::Name(_) => ETypedIdent::Name(Position::zero()), + ETypedIdent::Type(inner_err, _) => { + ETypedIdent::Type(inner_err.remove_spaces(arena), Position::zero()) + } + ETypedIdent::IndentType(_) => ETypedIdent::IndentType(Position::zero()), + ETypedIdent::Identifier(_) => ETypedIdent::Identifier(Position::zero()), + } + } +} diff --git a/crates/compiler/parse/src/state.rs b/crates/compiler/parse/src/state.rs index 7e4207584f4..c73318bd022 100644 --- a/crates/compiler/parse/src/state.rs +++ b/crates/compiler/parse/src/state.rs @@ -129,6 +129,10 @@ impl<'a> State<'a> { pub fn len_region(&self, length: u32) -> Region { Region::new(self.pos(), self.pos().bump_column(length)) } + + pub fn is_at_start_of_file(&self) -> bool { + self.offset == 0 + } } impl<'a> fmt::Debug for State<'a> { diff --git a/crates/compiler/parse/src/test_helpers.rs b/crates/compiler/parse/src/test_helpers.rs index 5df2fbb2639..91d1f24b199 100644 --- a/crates/compiler/parse/src/test_helpers.rs +++ b/crates/compiler/parse/src/test_helpers.rs @@ -22,7 +22,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SourceError<'a, SyntaxError<'a>>> { - let state = State::new(input.trim().as_bytes()); + let state = State::new(input.as_bytes()); match crate::expr::test_parse_expr(0, arena, state.clone()) { Ok(loc_expr) => Ok(loc_expr), @@ -31,7 +31,7 @@ pub fn parse_loc_with<'a>( } pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); + let state = State::new(input.as_bytes()); parse_module_defs(arena, state, Defs::default()) } @@ -40,7 +40,7 @@ pub fn parse_header_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); + let state = State::new(input.as_bytes()); match crate::module::parse_header(arena, state.clone()) { Ok((header, _)) => Ok(header), diff --git a/crates/compiler/test_gen/src/gen_tags.rs b/crates/compiler/test_gen/src/gen_tags.rs index ce632a98bc7..df25fae4460 100644 --- a/crates/compiler/test_gen/src/gen_tags.rs +++ b/crates/compiler/test_gen/src/gen_tags.rs @@ -2176,8 +2176,8 @@ fn refcount_nullable_unwrapped_needing_no_refcount_issue_5027() { await : Effect, (Str -> Effect) -> Effect await = \fx, cont -> after - fx - cont + fx + cont succeed : {} -> Effect succeed = \{} -> (\{} -> "success") diff --git a/crates/compiler/test_syntax/fuzz/Cargo.toml b/crates/compiler/test_syntax/fuzz/Cargo.toml index b1389d04d78..42e896950ae 100644 --- a/crates/compiler/test_syntax/fuzz/Cargo.toml +++ b/crates/compiler/test_syntax/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true [dependencies] test_syntax = { path = "../../test_syntax" } +roc_parse = { path = "../../parse" } bumpalo = { version = "3.12.0", features = ["collections"] } libfuzzer-sys = "0.4" diff --git a/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs b/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs index 1811947ec8b..1a5b07598e3 100644 --- a/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs +++ b/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs @@ -1,14 +1,18 @@ #![no_main] -use libfuzzer_sys::fuzz_target; use bumpalo::Bump; +use libfuzzer_sys::fuzz_target; +use roc_parse::ast::Malformed; use test_syntax::test_helpers::Input; fuzz_target!(|data: &[u8]| { if let Ok(input) = std::str::from_utf8(data) { let input = Input::Expr(input); let arena = Bump::new(); - if input.parse_in(&arena).is_ok() { - input.check_invariants(|_| (), true); + let ast = input.parse_in(&arena); + if let Ok(ast) = ast { + if !ast.is_malformed() { + input.check_invariants(|_| (), true); + } } } }); diff --git a/crates/compiler/test_syntax/src/bin/minimize.rs b/crates/compiler/test_syntax/src/bin/minimize.rs new file mode 100644 index 00000000000..404a45b5a67 --- /dev/null +++ b/crates/compiler/test_syntax/src/bin/minimize.rs @@ -0,0 +1,23 @@ +use test_syntax::{minimize::print_minimizations, test_helpers::InputKind}; + +fn main() { + let args = std::env::args().collect::>(); + if args.len() != 3 { + eprintln!("Usage: {} [expr|full|moduledefs|header] ", args[0]); + std::process::exit(1); + } + + let kind = match args[1].as_str() { + "expr" => InputKind::Expr, + "full" => InputKind::Full, + "moduledefs" => InputKind::ModuleDefs, + "header" => InputKind::Header, + _ => { + eprintln!("Invalid input kind: {}", args[1]); + std::process::exit(1); + } + }; + + let text = std::fs::read_to_string(&args[2]).unwrap(); + print_minimizations(&text, kind); +} diff --git a/crates/compiler/test_syntax/src/lib.rs b/crates/compiler/test_syntax/src/lib.rs index 6687a71727b..e929d8cfe6c 100644 --- a/crates/compiler/test_syntax/src/lib.rs +++ b/crates/compiler/test_syntax/src/lib.rs @@ -1 +1,2 @@ +pub mod minimize; pub mod test_helpers; diff --git a/crates/compiler/test_syntax/src/minimize.rs b/crates/compiler/test_syntax/src/minimize.rs new file mode 100644 index 00000000000..d8fd4647d41 --- /dev/null +++ b/crates/compiler/test_syntax/src/minimize.rs @@ -0,0 +1,204 @@ +use crate::test_helpers::{Input, InputKind}; +use bumpalo::Bump; +use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces}; + +pub fn print_minimizations(text: &str, kind: InputKind) { + let Some(original_error) = round_trip_once_and_extract_error(text, kind) else { + eprintln!("No error found"); + return; + }; + + eprintln!("Error found: {}", original_error); + eprintln!("Proceeding with minimization"); + + let mut s = text.to_string(); + + loop { + let mut found = false; + for update in candidate_minimizations(s.clone()) { + let mut new_s = String::with_capacity(s.len()); + let mut offset = 0; + for (start, end, replacement) in update.replacements.clone() { + new_s.push_str(&s[offset..start]); + new_s.push_str(&replacement); + offset = end; + } + new_s.push_str(&s[offset..]); + + assert!( + new_s.len() < s.len(), + "replacements: {:?}", + update.replacements + ); + + if let Some(result) = round_trip_once_and_extract_error(&new_s, kind) { + if result == original_error { + eprintln!("Successfully minimized, new length: {}", new_s.len()); + s = new_s; + found = true; + break; + } + } + } + + if !found { + eprintln!("No more minimizations found"); + break; + } + } + + eprintln!("Final result:"); + println!("{}", s); +} + +fn round_trip_once_and_extract_error(text: &str, kind: InputKind) -> Option { + let input = kind.with_text(text); + let res = std::panic::catch_unwind(|| round_trip_once(input)); + + match res { + Ok(res) => res, + Err(e) => { + if let Some(s) = e.downcast_ref::<&'static str>() { + return Some(s.to_string()); + } + if let Some(s) = e.downcast_ref::() { + return Some(s.clone()); + } + Some("Panic during parsing".to_string()) + } + } +} + +fn round_trip_once(input: Input<'_>) -> Option { + let arena = Bump::new(); + + let actual = match input.parse_in(&arena) { + Ok(a) => a, + Err(e) => { + return Some(format!( + "Initial parse failed: {:?}", + e.remove_spaces(&arena) + )) + } // todo: strip pos info, use the error + }; + + if actual.is_malformed() { + return Some("Initial parse is malformed".to_string()); + } + + let output = actual.format(); + + let reparsed_ast = match output.as_ref().parse_in(&arena) { + Ok(r) => r, + Err(e) => return Some(format!("Reparse failed: {:?}", e.remove_spaces(&arena))), + }; + + let ast_normalized = actual.remove_spaces(&arena); + let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena); + + if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") { + return Some("Different ast".to_string()); + } + + None +} + +struct Update { + replacements: Vec<(usize, usize, String)>, +} + +fn candidate_minimizations(s: String) -> Box> { + let mut line_offsets = vec![0]; + line_offsets.extend(s.match_indices('\n').map(|(i, _)| i + 1)); + let line_count = line_offsets.len(); + let s_len = s.len(); + + let line_indents = line_offsets + .iter() + .map(|&offset| s[offset..].chars().take_while(|&c| c == ' ').count()) + .collect::>(); + + let line_offsets_clone = line_offsets.clone(); + + // first, try to remove every group of 1, 2, 3, ... lines - in reverse order (so, trying removing n lines first, then n-1, etc) + let line_removals = (1..=line_count).rev().flat_map(move |n| { + let line_offsets_clone = line_offsets.clone(); + (0..line_count - n).map(move |start| { + let end = start + n; + let start_offset = line_offsets_clone[start]; + let end_offset = line_offsets_clone[end]; + let replacement = String::new(); + let replacements = vec![(start_offset, end_offset, replacement)]; + Update { replacements } + }) + }); + + let line_offsets = line_offsets_clone; + let line_offsets_clone = line_offsets.clone(); + + // then, try to dedent every group of 1, 2, 3, ... lines - in reverse order (so, trying dedenting n lines first, then n-1, etc) + // just remove one space at a time, for now + let line_dedents = (1..=line_count).rev().flat_map(move |n| { + let line_offsets_clone = line_offsets.clone(); + let line_indents_clone = line_indents.clone(); + (0..line_count - n).filter_map(move |start| { + // first check if all lines are either zero-width or have greater than zero indent + let end = start + n; + for i in start..end { + if line_indents_clone[i] == 0 + && line_offsets_clone[i] + 1 + < line_offsets_clone.get(i + 1).cloned().unwrap_or(s_len) + { + return None; + } + } + + let mut replacements = vec![]; + for i in start..end { + let offset = line_offsets_clone[i]; + let indent = line_indents_clone[i]; + if indent > 0 { + replacements.push((offset, offset + 1, String::new())); + } + } + Some(Update { replacements }) + }) + }); + + // then, try to select every range of 1, 2, 3, ... lines - in normal order this time! + // we remove the lines before and after the range + let line_selects = (1..line_count - 1).flat_map(move |n| { + assert!(n > 0); + let line_offsets_clone = line_offsets_clone.clone(); + (0..line_count - n).map(move |start| { + let end = start + n; + let start_offset = line_offsets_clone[start]; + let end_offset = line_offsets_clone[end]; + assert!(end_offset > start_offset); + assert!(start_offset > 0 || end_offset < s_len); + let replacements = vec![ + (0, start_offset, String::new()), + (end_offset, s_len, String::new()), + ]; + Update { replacements } + }) + }); + + // then, try to remove every range of 1, 2, 3, ... characters - in reverse order (so, trying removing n characters first, then n-1, etc) + let charseq_removals = (1..s.len()).rev().flat_map(move |n| { + (0..s.len() - n).map(move |start| { + let end = start + n; + let replacement = String::new(); + let replacements = vec![(start, end, replacement)]; + Update { replacements } + }) + }); + + Box::new( + line_removals + .chain(line_dedents) + .chain(line_selects) + .chain(charseq_removals) + .filter(|u| !u.replacements.is_empty()), + ) +} diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index 96b5ae2ef80..64411b54e63 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -4,12 +4,12 @@ use roc_parse::{ ast::{Defs, Expr, Malformed, Module}, module::parse_module_defs, parser::{Parser, SyntaxError}, + remove_spaces::RemoveSpaces, state::State, test_helpers::{parse_defs_with, parse_expr_with, parse_header_with}, }; use roc_test_utils::assert_multiline_str_eq; -use roc_fmt::spaces::RemoveSpaces; use roc_fmt::Buf; /// Source code to parse. Usually in the form of a test case. @@ -28,6 +28,25 @@ pub enum Input<'a> { Full(&'a str), } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum InputKind { + Header, + ModuleDefs, + Expr, + Full, +} + +impl InputKind { + pub fn with_text(self, text: &str) -> Input { + match self { + InputKind::Header => Input::Header(text), + InputKind::ModuleDefs => Input::ModuleDefs(text), + InputKind::Expr => Input::Expr(text), + InputKind::Full => Input::Full(text), + } + } +} + // Owned version of `Input` #[derive(Debug, Clone, PartialEq, Eq)] pub enum InputOwned { @@ -38,7 +57,7 @@ pub enum InputOwned { } impl InputOwned { - fn as_ref(&self) -> Input { + pub fn as_ref(&self) -> Input { match self { InputOwned::Header(s) => Input::Header(s), InputOwned::ModuleDefs(s) => Input::ModuleDefs(s), @@ -64,7 +83,7 @@ pub enum Output<'a> { } impl<'a> Output<'a> { - fn format(&self) -> InputOwned { + pub fn format(&self) -> InputOwned { let arena = Bump::new(); let mut buf = Buf::new_in(&arena); match self { @@ -172,7 +191,7 @@ impl<'a> Input<'a> { let (header, defs) = header.upgrade_header_imports(arena); - let module_defs = parse_module_defs(arena, state, defs).unwrap(); + let module_defs = parse_module_defs(arena, state, defs)?; Ok(Output::Full { header, diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast new file mode 100644 index 00000000000..88ad924cba0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast @@ -0,0 +1 @@ +Expr(BackpassContinue(@8), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc new file mode 100644 index 00000000000..38c82263c85 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc @@ -0,0 +1,2 @@ +u:i +e<-x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast new file mode 100644 index 00000000000..9ac9315f17d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast @@ -0,0 +1 @@ +Expr(BadExprEnd(@4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast index 8fa9f087166..537223e3eba 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast @@ -1 +1 @@ -Expr(DefMissingFinalExpr2(Start(@11), @11), @0) \ No newline at end of file +Expr(IndentEnd(@11), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast new file mode 100644 index 00000000000..fecbed76f72 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast @@ -0,0 +1 @@ +Expr(BadExprEnd(@3), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast index 83572402651..d2f1c32c260 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast @@ -1 +1 @@ -Expr(Start(@0), @0) \ No newline at end of file +Expr(IndentEnd(@12), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast index c7af95fd959..72cd39a508e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast @@ -1 +1 @@ -Expr(If(Else(@16), @0), @0) \ No newline at end of file +Expr(If(Else(@17), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast index 98c1ed8a32a..92672b11ae8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast @@ -1 +1 @@ -Expr(List(End(@6), @0), @0) \ No newline at end of file +Expr(List(End(@7), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast new file mode 100644 index 00000000000..55da9422601 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast @@ -0,0 +1 @@ +Expr(InParens(Expr(BadExprEnd(@8), @5), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc new file mode 100644 index 00000000000..eeaad94b065 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc @@ -0,0 +1,3 @@ +a : e +Na := e +e0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast new file mode 100644 index 00000000000..0a44a046d5e --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast @@ -0,0 +1 @@ +Expr(BadExprEnd(@11), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast index 73f840b6e9b..e45c50b689d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast index e45c50b689d..5d5dd4298cb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@6), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast index a465d965e4e..810e58ea1ca 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@2), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@3), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast index 73f840b6e9b..e45c50b689d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast index 143c16842e4..8289168c158 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast @@ -1 +1 @@ -Expr(Type(TRecord(End(@13), @4), @4), @0) \ No newline at end of file +Expr(Type(TRecord(End(@14), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast index 6b9fe2cc71b..0d2c7c0bfec 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast @@ -1 +1 @@ -Expr(Type(TRecord(End(@5), @4), @4), @0) \ No newline at end of file +Expr(Type(TRecord(End(@6), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast index 6965e362028..9851faa0406 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast @@ -1 +1 @@ -Expr(Type(TRecord(End(@16), @4), @4), @0) \ No newline at end of file +Expr(Type(TRecord(End(@17), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast index 7c24e2662e7..29e52c819ab 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast @@ -1 +1 @@ -Expr(Type(TTagUnion(End(@9), @4), @4), @0) \ No newline at end of file +Expr(Type(TTagUnion(End(@10), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast index 3375d4db330..7eacf4383a5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast @@ -1 +1 @@ -Expr(Type(TTagUnion(End(@5), @4), @4), @0) \ No newline at end of file +Expr(Type(TTagUnion(End(@6), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast index 22658fc6185..eedba7ebfb2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast @@ -1 +1 @@ -Expr(Type(TInParens(End(@9), @4), @4), @0) \ No newline at end of file +Expr(Type(TInParens(End(@10), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast index 1afa91bf38d..e89dbbe6a6b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast @@ -1 +1 @@ -Expr(Type(TInParens(End(@5), @4), @4), @0) \ No newline at end of file +Expr(Type(TInParens(End(@6), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast index af23c953796..37c92c7aa61 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Arrow(@10), @4), @0) \ No newline at end of file +Expr(Closure(IndentArrow(@10), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast new file mode 100644 index 00000000000..6bafebb5637 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast @@ -0,0 +1 @@ +Expr(Import(EndNewline(@15), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc new file mode 100644 index 00000000000..c9257006626 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc @@ -0,0 +1 @@ +import svg.Path a diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast index 5f421211a3e..5b5d49fa8ce 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast @@ -1 +1 @@ -Expr(When(Arrow(@26), @0), @0) \ No newline at end of file +Expr(When(IndentPattern(@26), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast index 3d9b09ae774..9e066cfe7e6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast @@ -1 +1 @@ -Expr(BadExprEnd(@22), @0) \ No newline at end of file +Expr(BadOperator("->", @24), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast index d0b15de0e26..1c7c9f7bdd8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast @@ -1 +1 @@ -Expr(When(Branch(BadOperator("->", @34), @19), @0), @0) \ No newline at end of file +Expr(When(IndentPattern(@34), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast index 629914f2c0f..0078479cabd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast @@ -1 +1 @@ -Expr(When(Branch(BadOperator("->", @28), @19), @0), @0) \ No newline at end of file +Expr(When(IndentPattern(@28), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast index 24766ae346d..0a44a046d5e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast @@ -1 +1 @@ -Expr(DefMissingFinalExpr2(ElmStyleFunction(@18-22, @23), @11), @0) \ No newline at end of file +Expr(BadExprEnd(@11), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast index 47ec4d27f66..f24ddecfd0d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast @@ -1 +1 @@ -Expr(BadOperator("->", @9), @0) \ No newline at end of file +Expr(BadExprEnd(@8), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc new file mode 100644 index 00000000000..62430a76660 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc @@ -0,0 +1,3 @@ +when x is + bar.and -> 1 + _ -> 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast index 526bf8a5cd1..a71960a7128 100644 --- a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @14-21 SpaceBefore( - Malformed( - "bar.and", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-21 SpaceBefore( + Malformed( + "bar.and", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @25-26 Num( + "1", ), - ], - value: @25-26 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @31-32 SpaceBefore( - Underscore( - "", + guard: None, + }, + WhenBranch { + patterns: [ + @31-32 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @36-37 Num( + "4", ), - ], - value: @36-37 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc new file mode 100644 index 00000000000..d7120ecf860 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc @@ -0,0 +1,3 @@ +when x is + Foo.and -> 1 + _ -> 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast index fa23b429e93..97e2e5b5944 100644 --- a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @14-21 SpaceBefore( - Malformed( - "Foo.and", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-21 SpaceBefore( + Malformed( + "Foo.and", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @25-26 Num( + "1", ), - ], - value: @25-26 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @31-32 SpaceBefore( - Underscore( - "", + guard: None, + }, + WhenBranch { + patterns: [ + @31-32 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @36-37 Num( + "4", ), - ], - value: @36-37 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast index 03168073e5f..ef7a2615c7c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast @@ -1,63 +1,68 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-43, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-4 "Hash", - vars: [], - }, - loc_implements: @5-15 Implements, - members: [ - AbilityMember { - name: @18-22 SpaceBefore( - "hash", - [ - Newline, - ], - ), - typ: @25-43 Function( - [ - @25-26 SpaceAfter( - BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-43, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-4 "Hash", + vars: [], + }, + loc_implements: @5-15 Implements, + members: [ + AbilityMember { + name: @18-22 SpaceBefore( + "hash", + [ + Newline, + ], + ), + typ: @25-43 Function( + [ + @25-26 SpaceAfter( + BoundVariable( + "a", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + @40-43 Apply( + "", + "U64", + [], ), - ], - @40-43 Apply( - "", - "U64", - [], ), - ), - }, - ], - }, - ], - value_defs: [], - }, - @45-46 SpaceBefore( - Num( - "1", + }, + ], + }, + ], + value_defs: [], + }, + @45-46 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast index 9203211925a..aeeaa7d0f86 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast @@ -1,78 +1,83 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-52, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-4 "Hash", - vars: [], - }, - loc_implements: @5-15 Implements, - members: [ - AbilityMember { - name: @18-22 SpaceBefore( - "hash", - [ - Newline, - ], - ), - typ: @25-33 Function( - [ - @25-26 BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-52, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-4 "Hash", + vars: [], + }, + loc_implements: @5-15 Implements, + members: [ + AbilityMember { + name: @18-22 SpaceBefore( + "hash", + [ + Newline, + ], + ), + typ: @25-33 Function( + [ + @25-26 BoundVariable( + "a", + ), + ], + @30-33 Apply( + "", + "U64", + [], ), - ], - @30-33 Apply( - "", - "U64", - [], ), - ), - }, - AbilityMember { - name: @36-41 SpaceBefore( - "hash2", - [ - Newline, - ], - ), - typ: @44-52 Function( - [ - @44-45 BoundVariable( - "a", + }, + AbilityMember { + name: @36-41 SpaceBefore( + "hash2", + [ + Newline, + ], + ), + typ: @44-52 Function( + [ + @44-45 BoundVariable( + "a", + ), + ], + @49-52 Apply( + "", + "U64", + [], ), - ], - @49-52 Apply( - "", - "U64", - [], ), - ), - }, - ], - }, - ], - value_defs: [], - }, - @54-55 SpaceBefore( - Num( - "1", + }, + ], + }, + ], + value_defs: [], + }, + @54-55 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc new file mode 100644 index 00000000000..537edf32032 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc @@ -0,0 +1,3 @@ +Hash implements hash : a -> U64 where a implements Hash + +1 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast index 2847f04b1dc..0bc39b910f7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast @@ -1,67 +1,72 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-55, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-4 "Hash", - vars: [], - }, - loc_implements: @5-15 Implements, - members: [ - AbilityMember { - name: @16-20 "hash", - typ: @23-55 Where( - @23-31 Function( - [ - @23-24 BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-55, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-4 "Hash", + vars: [], + }, + loc_implements: @5-15 Implements, + members: [ + AbilityMember { + name: @16-20 "hash", + typ: @23-55 Where( + @23-31 Function( + [ + @23-24 BoundVariable( + "a", + ), + ], + @28-31 Apply( + "", + "U64", + [], ), - ], - @28-31 Apply( - "", - "U64", - [], ), + [ + @38-55 ImplementsClause { + var: @38-39 "a", + abilities: [ + @51-55 Apply( + "", + "Hash", + [], + ), + ], + }, + ], ), - [ - @38-55 ImplementsClause { - var: @38-39 "a", - abilities: [ - @51-55 Apply( - "", - "Hash", - [], - ), - ], - }, - ], - ), - }, - ], - }, - ], - value_defs: [], - }, - @57-58 SpaceBefore( - Num( - "1", + }, + ], + }, + ], + value_defs: [], + }, + @57-58 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc new file mode 100644 index 00000000000..6751436b6a2 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc @@ -0,0 +1,5 @@ +Ab1 implements ab1 : a -> {} where a implements Ab1 + +Ab2 implements ab2 : a -> {} where a implements Ab2 + +1 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast index d4285ec53d1..a4f1abe8e1e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast @@ -1,110 +1,115 @@ -Defs( - Defs { - tags: [ - Index(0), - Index(1), - ], - regions: [ - @0-51, - @53-104, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-3 "Ab1", - vars: [], - }, - loc_implements: @4-14 Implements, - members: [ - AbilityMember { - name: @15-18 "ab1", - typ: @21-51 Where( - @21-28 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + Index(1), + ], + regions: [ + @0-51, + @53-104, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-3 "Ab1", + vars: [], + }, + loc_implements: @4-14 Implements, + members: [ + AbilityMember { + name: @15-18 "ab1", + typ: @21-51 Where( + @21-28 Function( + [ + @21-22 BoundVariable( + "a", + ), + ], + @26-28 Record { + fields: [], + ext: None, + }, + ), [ - @21-22 BoundVariable( - "a", - ), + @35-51 ImplementsClause { + var: @35-36 "a", + abilities: [ + @48-51 Apply( + "", + "Ab1", + [], + ), + ], + }, ], - @26-28 Record { - fields: [], - ext: None, - }, ), - [ - @35-51 ImplementsClause { - var: @35-36 "a", - abilities: [ - @48-51 Apply( - "", - "Ab1", - [], + }, + ], + }, + Ability { + header: TypeHeader { + name: @53-56 "Ab2", + vars: [], + }, + loc_implements: @57-67 Implements, + members: [ + AbilityMember { + name: @68-71 "ab2", + typ: @74-104 Where( + @74-81 Function( + [ + @74-75 BoundVariable( + "a", ), ], - }, - ], - ), - }, - ], - }, - Ability { - header: TypeHeader { - name: @53-56 "Ab2", - vars: [], - }, - loc_implements: @57-67 Implements, - members: [ - AbilityMember { - name: @68-71 "ab2", - typ: @74-104 Where( - @74-81 Function( + @79-81 Record { + fields: [], + ext: None, + }, + ), [ - @74-75 BoundVariable( - "a", - ), + @88-104 ImplementsClause { + var: @88-89 "a", + abilities: [ + @101-104 Apply( + "", + "Ab2", + [], + ), + ], + }, ], - @79-81 Record { - fields: [], - ext: None, - }, ), - [ - @88-104 ImplementsClause { - var: @88-89 "a", - abilities: [ - @101-104 Apply( - "", - "Ab2", - [], - ), - ], - }, - ], - ), - }, - ], - }, - ], - value_defs: [], - }, - @106-107 SpaceBefore( - Num( - "1", + }, + ], + }, + ], + value_defs: [], + }, + @106-107 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast index 52cac4a3cac..9ad1418cac9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast @@ -1,78 +1,83 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-49, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-8 RecordDestructure( - [ - @2-3 Identifier { - ident: "x", - }, - @5-7 Identifier { - ident: "y", - }, - ], - ), - ann_type: @11-14 Apply( - "", - "Foo", - [], - ), - comment: None, - body_pattern: @15-23 RecordDestructure( - [ - @17-18 Identifier { - ident: "x", - }, - @20-21 Identifier { - ident: "y", - }, - ], - ), - body_expr: @26-49 Record( - [ - @28-37 RequiredValue( - @28-29 "x", - [], - @32-37 Str( - PlainLine( - "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-49, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-8 RecordDestructure( + [ + @2-3 Identifier { + ident: "x", + }, + @5-7 Identifier { + ident: "y", + }, + ], + ), + ann_type: @11-14 Apply( + "", + "Foo", + [], + ), + comment: None, + body_pattern: @15-23 RecordDestructure( + [ + @17-18 Identifier { + ident: "x", + }, + @20-22 Identifier { + ident: "y", + }, + ], + ), + body_expr: @26-49 Record( + [ + @28-37 RequiredValue( + @28-29 "x", + [], + @32-37 Str( + PlainLine( + "foo", + ), ), ), - ), - @39-47 RequiredValue( - @39-40 "y", - [], - @43-47 Float( - "3.14", + @39-47 RequiredValue( + @39-40 "y", + [], + @43-47 Float( + "3.14", + ), ), - ), - ], - ), - }, - ], - }, - @51-52 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], + ), + }, + ], }, - [ - Newline, - Newline, - ], + @51-52 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast index 6b6a39c5b03..1eec16a8705 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast @@ -1,79 +1,84 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-46, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-8 Apply( - @0-6 Tag( - "UserId", - ), - [ - @7-8 Identifier { - ident: "x", - }, - ], - ), - ann_type: @11-25 TagUnion { - ext: None, - tags: [ - @13-23 Apply { - name: @13-19 "UserId", - args: [ - @20-23 Apply( - "", - "I64", - [], - ), - ], - }, - ], - }, - comment: None, - body_pattern: @26-34 Apply( - @26-32 Tag( - "UserId", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-46, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-8 Apply( + @0-6 Tag( + "UserId", + ), + [ + @7-8 Identifier { + ident: "x", + }, + ], ), - [ - @33-34 Identifier { - ident: "x", - }, - ], - ), - body_expr: @37-46 Apply( - @37-43 Tag( - "UserId", + ann_type: @11-25 TagUnion { + ext: None, + tags: [ + @13-23 Apply { + name: @13-19 "UserId", + args: [ + @20-23 Apply( + "", + "I64", + [], + ), + ], + }, + ], + }, + comment: None, + body_pattern: @26-34 Apply( + @26-32 Tag( + "UserId", + ), + [ + @33-34 Identifier { + ident: "x", + }, + ], ), - [ - @44-46 Num( - "42", + body_expr: @37-46 Apply( + @37-43 Tag( + "UserId", ), - ], - Space, - ), - }, - ], - }, - @48-49 SpaceBefore( - Var { - module_name: "", - ident: "x", + [ + @44-46 Num( + "42", + ), + ], + Space, + ), + }, + ], }, - [ - Newline, - Newline, - ], + @48-49 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast index 49768c21563..3ac22b49de4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast @@ -1,70 +1,75 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-41, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-8 Tuple( - [ - @2-3 Identifier { - ident: "x", - }, - @5-6 Identifier { - ident: "y", - }, - ], - ), - ann_type: @11-14 Apply( - "", - "Foo", - [], - ), - comment: None, - body_pattern: @15-23 Tuple( - [ - @17-18 Identifier { - ident: "x", - }, - @20-21 Identifier { - ident: "y", - }, - ], - ), - body_expr: @26-41 Tuple( - [ - @28-33 Str( - PlainLine( - "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-41, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-8 Tuple( + [ + @2-3 Identifier { + ident: "x", + }, + @5-6 Identifier { + ident: "y", + }, + ], + ), + ann_type: @11-14 Apply( + "", + "Foo", + [], + ), + comment: None, + body_pattern: @15-23 Tuple( + [ + @17-18 Identifier { + ident: "x", + }, + @20-21 Identifier { + ident: "y", + }, + ], + ), + body_expr: @26-41 Tuple( + [ + @28-33 Str( + PlainLine( + "foo", + ), ), - ), - @35-39 Float( - "3.14", - ), - ], - ), - }, - ], - }, - @43-44 SpaceBefore( - Var { - module_name: "", - ident: "x", + @35-39 Float( + "3.14", + ), + ], + ), + }, + ], }, - [ - Newline, - Newline, - ], + @43-44 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc new file mode 100644 index 00000000000..8ebd4435d47 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc @@ -0,0 +1,9 @@ +## first line of docs +## second line +## third line +## fourth line +## +## sixth line after doc new line +x = 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast index c74acaf8995..af12c619759 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @107-112, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @107-108 Identifier { - ident: "x", - }, - @111-112 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @107-112, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @107-108 Identifier { + ident: "x", + }, + @111-112 Num( + "5", + ), ), + ], + }, + @114-116 SpaceBefore( + Num( + "42", ), - ], - }, - @114-116 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ DocComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast deleted file mode 100644 index c5b57b1c723..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast +++ /dev/null @@ -1,36 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-4, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "a", - }, - @3-4 SpaceBefore( - BoundVariable( - "c", - ), - [ - Newline, - ], - ), - ), - ], - }, - @5-6 Num( - "0", - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc deleted file mode 100644 index c1feee45382..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc +++ /dev/null @@ -1,3 +0,0 @@ -f - -5 - 2 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast deleted file mode 100644 index 1fb8443efb1..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast +++ /dev/null @@ -1,25 +0,0 @@ -Apply( - @0-1 SpaceAfter( - Var { - module_name: "", - ident: "f", - }, - [ - Newline, - ], - ), - [ - @2-4 Num( - "-5", - ), - @5-6 SpaceBefore( - Num( - "2", - ), - [ - Newline, - ], - ), - ], - Space, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc deleted file mode 100644 index 2f398535c72..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc +++ /dev/null @@ -1,3 +0,0 @@ -f --5 -2 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc deleted file mode 100644 index 6b3df9a06e6..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc +++ /dev/null @@ -1,3 +0,0 @@ -a -&& (\x -> x) - 8 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast deleted file mode 100644 index 73825d72110..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast +++ /dev/null @@ -1,35 +0,0 @@ -BinOps( - [ - ( - @0-1 Var { - module_name: "", - ident: "a", - }, - @2-4 And, - ), - ], - @5-12 Apply( - @5-10 Closure( - [ - @6-7 Identifier { - ident: "x", - }, - ], - @9-10 Var { - module_name: "", - ident: "x", - }, - ), - [ - @11-12 SpaceBefore( - Num( - "8", - ), - [ - Newline, - ], - ), - ], - Space, - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc deleted file mode 100644 index f58fe8a2829..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc +++ /dev/null @@ -1,2 +0,0 @@ -a && \x->x -8 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc new file mode 100644 index 00000000000..0bddb5ce242 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc @@ -0,0 +1,8 @@ +Model position : { + evaluated : Set position, + openSet : Set position, + costs : Dict.Dict position F64, + cameFrom : Dict.Dict position position, +} + +a \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast new file mode 100644 index 00000000000..2e0614b3ba0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast @@ -0,0 +1,130 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-164, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Alias { + header: TypeHeader { + name: @0-5 "Model", + vars: [ + @6-14 Identifier { + ident: "position", + }, + ], + }, + ann: @21-164 Record { + fields: [ + @23-47 SpaceAfter( + RequiredValue( + @23-32 "evaluated", + [], + @35-47 Apply( + "", + "Set", + [ + @39-47 BoundVariable( + "position", + ), + ], + ), + ), + [ + Newline, + ], + ), + @54-76 SpaceAfter( + RequiredValue( + @54-61 "openSet", + [], + @64-76 Apply( + "", + "Set", + [ + @68-76 BoundVariable( + "position", + ), + ], + ), + ), + [ + Newline, + ], + ), + @83-113 SpaceAfter( + RequiredValue( + @83-88 "costs", + [], + @91-113 Apply( + "Dict", + "Dict", + [ + @101-109 BoundVariable( + "position", + ), + @110-113 Apply( + "", + "F64", + [], + ), + ], + ), + ), + [ + Newline, + ], + ), + @120-158 SpaceAfter( + RequiredValue( + @120-128 "cameFrom", + [], + @131-158 Apply( + "Dict", + "Dict", + [ + @141-149 BoundVariable( + "position", + ), + @150-158 BoundVariable( + "position", + ), + ], + ), + ), + [ + Newline, + ], + ), + ], + ext: None, + }, + }, + ], + value_defs: [], + }, + @166-167 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc new file mode 100644 index 00000000000..bb3f03927d6 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc @@ -0,0 +1,8 @@ +Model position : + { evaluated : Set position + , openSet : Set position + , costs : Dict.Dict position F64 + , cameFrom : Dict.Dict position position + } + +a diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc deleted file mode 100644 index 11c39e4de96..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -foo = 1 # comment after diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc deleted file mode 100644 index 878f99f997a..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -Z # -h - : a -j \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast deleted file mode 100644 index e7bb56f7665..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast +++ /dev/null @@ -1,54 +0,0 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-7, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Alias { - header: TypeHeader { - name: @0-1 "Z", - vars: [ - @3-4 SpaceAfter( - SpaceBefore( - Identifier { - ident: "h", - }, - [ - LineComment( - "", - ), - ], - ), - [ - Newline, - ], - ), - ], - }, - ann: @6-7 BoundVariable( - "a", - ), - }, - ], - value_defs: [], - }, - @8-9 SpaceBefore( - Var { - module_name: "", - ident: "j", - }, - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc deleted file mode 100644 index 3737e7a2476..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc +++ /dev/null @@ -1,4 +0,0 @@ -Z# -h -:a -j \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast index 88b452be8ae..05b3ab13f85 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast @@ -1,163 +1,121 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), - Index(2147483651), - Index(2147483652), - ], - regions: [ - @0-12, - @13-28, - @29-45, - @46-74, - @75-101, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 1), - Slice(start = 2, length = 1), - Slice(start = 3, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - Slice(start = 4, length = 0), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-1 Underscore( - "", - ), - @4-12 Apply( - @4-9 Crash, - [ - @10-12 Str( - PlainLine( - "", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + Index(2147483651), + Index(2147483652), + ], + regions: [ + @0-12, + @13-28, + @29-45, + @46-74, + @75-101, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + Slice(start = 2, length = 1), + Slice(start = 3, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-1 Underscore( + "", + ), + @4-12 Apply( + @4-9 Crash, + [ + @10-12 Str( + PlainLine( + "", + ), ), - ), - ], - Space, - ), - ), - Body( - @13-14 Underscore( - "", + ], + Space, + ), ), - @17-28 Apply( - @17-22 Crash, - [ - @23-25 Str( - PlainLine( - "", + Body( + @13-14 Underscore( + "", + ), + @17-28 Apply( + @17-22 Crash, + [ + @23-25 Str( + PlainLine( + "", + ), ), - ), - @26-28 Str( - PlainLine( - "", + @26-28 Str( + PlainLine( + "", + ), ), - ), - ], - Space, - ), - ), - Body( - @29-30 Underscore( - "", - ), - @33-45 Apply( - @33-38 Crash, - [ - @39-41 Num( - "15", - ), - @42-45 Num( - "123", - ), - ], - Space, + ], + Space, + ), ), - ), - Body( - @46-47 Underscore( - "", + Body( + @29-30 Underscore( + "", + ), + @33-45 Apply( + @33-38 Crash, + [ + @39-41 Num( + "15", + ), + @42-45 Num( + "123", + ), + ], + Space, + ), ), - @50-74 Apply( - @50-53 Var { - module_name: "", - ident: "try", - }, - [ - @54-57 Var { + Body( + @46-47 Underscore( + "", + ), + @50-74 Apply( + @50-53 Var { module_name: "", - ident: "foo", + ident: "try", }, - @59-73 ParensAround( - Closure( - [ - @60-61 Underscore( - "", - ), - ], - @65-73 Apply( - @65-70 Crash, + [ + @54-57 Var { + module_name: "", + ident: "foo", + }, + @59-73 ParensAround( + Closure( [ - @71-73 Str( - PlainLine( - "", - ), + @60-61 Underscore( + "", ), ], - Space, - ), - ), - ), - ], - Space, - ), - ), - Body( - @75-76 Underscore( - "", - ), - @81-101 SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @81-93, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @81-82 Underscore( - "", - ), - @85-93 Apply( - @85-90 Crash, + @65-73 Apply( + @65-70 Crash, [ - @91-93 Str( + @71-73 Str( PlainLine( "", ), @@ -166,45 +124,92 @@ Defs( Space, ), ), - ], - }, - @96-101 SpaceBefore( - Crash, - [ - Newline, - ], - ), + ), + ], + Space, ), - [ - Newline, - ], ), - ), - ], - }, - @103-118 SpaceBefore( - Record( - [ - @105-116 RequiredValue( - @105-106 "f", - [], - @108-116 Apply( - @108-113 Crash, - [ - @114-116 Str( - PlainLine( - "", - ), + Body( + @75-76 Underscore( + "", + ), + @81-101 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @81-93, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @81-82 Underscore( + "", + ), + @85-93 Apply( + @85-90 Crash, + [ + @91-93 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + ], + }, + @96-101 SpaceBefore( + Crash, + [ + Newline, + ], ), + ), + [ + Newline, ], - Space, ), ), ], + }, + @103-118 SpaceBefore( + Record( + [ + @105-116 RequiredValue( + @105-106 "f", + [], + @108-116 Apply( + @108-113 Crash, + [ + @114-116 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + ], + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc new file mode 100644 index 00000000000..44de35e8d24 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc @@ -0,0 +1,3 @@ +dbg 1 == 1 + +4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast index e928afdee51..ef6e22eecb8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast @@ -1,24 +1,34 @@ -Dbg( - @4-10 BinOps( - [ - ( - @4-5 Num( +SpaceBefore( + SpaceAfter( + Dbg( + @5-11 BinOps( + [ + ( + @5-6 Num( + "1", + ), + @7-9 Equals, + ), + ], + @10-11 Num( "1", ), - @6-8 Equals, ), - ], - @9-10 Num( - "1", - ), - ), - @12-13 SpaceBefore( - Num( - "4", + @13-14 SpaceBefore( + Num( + "4", + ), + [ + Newline, + Newline, + ], + ), ), [ Newline, - Newline, ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc index c1dc7243cb7..fab415486da 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc @@ -1,3 +1,4 @@ + dbg 1 == 1 4 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast index 5ec65c119d8..8bb47ecd8d9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast @@ -1,26 +1,31 @@ -Dbg( - @4-16 Tuple( - [ - @5-6 Num( - "5", - ), - @12-15 SpaceBefore( - Num( - "666", +SpaceAfter( + Dbg( + @4-16 Tuple( + [ + @5-6 Num( + "5", + ), + @12-15 SpaceBefore( + Num( + "666", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + ), + @18-19 SpaceBefore( + Num( + "4", ), - ], - ), - @18-19 SpaceBefore( - Num( - "4", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast deleted file mode 100644 index 6d305929070..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast +++ /dev/null @@ -1,32 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-3, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "a", - }, - @2-3 BoundVariable( - "b", - ), - ), - ], - }, - @4-5 Var { - module_name: "", - ident: "i", - }, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc new file mode 100644 index 00000000000..c0294a60f05 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc @@ -0,0 +1,9 @@ +main = + a = "Foo" + Stdout.line! a + + printBar! + +printBar = + b = "Bar" + Stdout.line b diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast new file mode 100644 index 00000000000..5b346e9c0cc --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast @@ -0,0 +1,159 @@ +Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @24-150, + @176-266, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @24-28 Identifier { + ident: "main", + }, + @59-150 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @59-68, + @97-111, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @59-60 Identifier { + ident: "a", + }, + @63-68 Str( + PlainLine( + "Foo", + ), + ), + ), + Stmt( + @97-111 Apply( + @97-108 TaskAwaitBang( + Var { + module_name: "Stdout", + ident: "line", + }, + ), + [ + @110-111 Var { + module_name: "", + ident: "a", + }, + ], + Space, + ), + ), + ], + }, + @141-150 SpaceBefore( + TaskAwaitBang( + Var { + module_name: "", + ident: "printBar", + }, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + Body( + @176-184 Identifier { + ident: "printBar", + }, + @215-266 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @215-224, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @215-216 Identifier { + ident: "b", + }, + @219-224 Str( + PlainLine( + "Bar", + ), + ), + ), + ], + }, + @253-266 SpaceBefore( + Apply( + @253-264 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @265-266 Var { + module_name: "", + ident: "b", + }, + ], + Space, + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc new file mode 100644 index 00000000000..12e697a2942 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc @@ -0,0 +1,9 @@ + main = + a = "Foo" + Stdout.line! a + + printBar! + + printBar = + b = "Bar" + Stdout.line b diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast index 1adeb713433..2e1a546396d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast @@ -1,54 +1,59 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-36, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-5 Apply( - @0-5 Tag( - "Email", - ), - [ - @6-9 Identifier { - ident: "str", - }, - ], - ), - @12-36 Apply( - @12-17 Tag( - "Email", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-36, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-9 Apply( + @0-5 Tag( + "Email", + ), + [ + @6-9 Identifier { + ident: "str", + }, + ], ), - [ - @18-36 Str( - PlainLine( - "blah@example.com", - ), + @12-36 Apply( + @12-17 Tag( + "Email", ), - ], - Space, + [ + @18-36 Str( + PlainLine( + "blah@example.com", + ), + ), + ], + Space, + ), ), - ), - ], - }, - @37-40 SpaceBefore( - Var { - module_name: "", - ident: "str", + ], }, - [ - Newline, - ], + @37-40 SpaceBefore( + Var { + module_name: "", + ident: "str", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast index 49f839bc4d5..808b4c36fc7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @48-53, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @48-49 Identifier { - ident: "x", - }, - @52-53 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @48-53, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @48-49 Identifier { + ident: "x", + }, + @52-53 Num( + "5", + ), ), + ], + }, + @55-57 SpaceBefore( + Num( + "42", ), - ], - }, - @55-57 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc deleted file mode 100644 index e43d69f4a16..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -hosted Foo exposes [] imports [] generates Bar with [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc deleted file mode 100644 index 9fd98c5b23f..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -module [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc deleted file mode 100644 index 313246d3594..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -package [] {} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc deleted file mode 100644 index 04d71d738d4..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -platform "rtfeldman/blah" requires {} { main : {} } exposes [] packages {} imports [] provides [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc new file mode 100644 index 00000000000..3cc762b5501 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc @@ -0,0 +1 @@ +"" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast index a00db728aff..5929994e772 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast @@ -1,5 +1,10 @@ -Str( - PlainLine( - "", +SpaceAfter( + Str( + PlainLine( + "", + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc index d0ec1ce6d34..2f47c144bef 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc @@ -1,4 +1,3 @@ -expect - 1 == 1 +expect 1 == 1 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast index fb565853874..5ffea14bab9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast @@ -1,24 +1,50 @@ -Expect( - @7-13 BinOps( - [ - ( - @7-8 Num( - "1", - ), - @9-11 Equals, +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-13, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Expect { + condition: @7-13 BinOps( + [ + ( + @7-8 Num( + "1", + ), + @9-11 Equals, + ), + ], + @12-13 Num( + "1", + ), + ), + preceding_comment: …, + }, + ], + }, + @15-16 SpaceBefore( + Num( + "4", ), - ], - @12-13 Num( - "1", + [ + Newline, + Newline, + ], ), ), - @15-16 SpaceBefore( - Num( - "4", - ), - [ - Newline, - Newline, - ], - ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast new file mode 100644 index 00000000000..1fb2e93bc34 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast @@ -0,0 +1,453 @@ +Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-644, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Expect { + condition: @11-644 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @11-118, + @124-252, + @258-556, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @11-15 Identifier { + ident: "html", + }, + ann_type: @18-25 Apply( + "", + "Html", + [ + @23-25 Record { + fields: [], + ext: None, + }, + ], + ), + comment: None, + body_pattern: @30-34 Identifier { + ident: "html", + }, + body_expr: @45-118 SpaceBefore( + Apply( + @45-52 Tag( + "Element", + ), + [ + @53-56 Str( + PlainLine( + "a", + ), + ), + @57-59 Num( + "43", + ), + @60-105 List( + [ + @61-104 Apply( + @61-69 Tag( + "HtmlAttr", + ), + [ + @70-76 Str( + PlainLine( + "href", + ), + ), + @77-104 Str( + PlainLine( + "https://www.roc-lang.org/", + ), + ), + ], + Space, + ), + ], + ), + @106-118 List( + [ + @107-117 Apply( + @107-111 Tag( + "Text", + ), + [ + @112-117 Str( + PlainLine( + "Roc", + ), + ), + ], + Space, + ), + ], + ), + ], + Space, + ), + [ + Newline, + ], + ), + }, + AnnotatedBody { + ann_pattern: @124-130 Identifier { + ident: "actual", + }, + ann_type: @133-185 Record { + fields: [ + @135-160 RequiredValue( + @135-140 "nodes", + [], + @143-160 Apply( + "", + "List", + [ + @148-160 Apply( + "", + "RenderedNode", + [], + ), + ], + ), + ), + @162-183 RequiredValue( + @162-172 "siblingIds", + [], + @175-183 Apply( + "", + "List", + [ + @180-183 Apply( + "", + "U64", + [], + ), + ], + ), + ), + ], + ext: None, + }, + comment: None, + body_pattern: @190-196 Identifier { + ident: "actual", + }, + body_expr: @207-252 SpaceBefore( + Apply( + @207-217 Var { + module_name: "", + ident: "indexNodes", + }, + [ + @218-247 Record( + [ + @220-229 RequiredValue( + @220-225 "nodes", + [], + @227-229 List( + [], + ), + ), + @231-245 RequiredValue( + @231-241 "siblingIds", + [], + @243-245 List( + [], + ), + ), + ], + ), + @248-252 Var { + module_name: "", + ident: "html", + }, + ], + Space, + ), + [ + Newline, + ], + ), + }, + AnnotatedBody { + ann_pattern: @258-266 Identifier { + ident: "expected", + }, + ann_type: @269-321 Record { + fields: [ + @271-296 RequiredValue( + @271-276 "nodes", + [], + @279-296 Apply( + "", + "List", + [ + @284-296 Apply( + "", + "RenderedNode", + [], + ), + ], + ), + ), + @298-319 RequiredValue( + @298-308 "siblingIds", + [], + @311-319 Apply( + "", + "List", + [ + @316-319 Apply( + "", + "U64", + [], + ), + ], + ), + ), + ], + ext: None, + }, + comment: None, + body_pattern: @326-334 Identifier { + ident: "expected", + }, + body_expr: @337-556 Record( + Collection { + items: [ + @347-524 SpaceBefore( + RequiredValue( + @347-352 "nodes", + [], + @354-524 List( + Collection { + items: [ + @368-386 SpaceBefore( + Apply( + @368-380 Tag( + "RenderedText", + ), + [ + @381-386 Str( + PlainLine( + "Roc", + ), + ), + ], + Space, + ), + [ + Newline, + ], + ), + @400-513 SpaceBefore( + Apply( + @400-415 Tag( + "RenderedElement", + ), + [ + @416-419 Str( + PlainLine( + "a", + ), + ), + @420-509 RecordUpdate { + update: @422-440 Var { + module_name: "", + ident: "emptyRenderedAttrs", + }, + fields: [ + @443-507 RequiredValue( + @443-452 "htmlAttrs", + [], + @454-507 Apply( + @454-467 Var { + module_name: "Dict", + ident: "fromList", + }, + [ + @468-507 List( + [ + @469-506 Tuple( + [ + @470-476 Str( + PlainLine( + "href", + ), + ), + @478-505 Str( + PlainLine( + "https://www.roc-lang.org/", + ), + ), + ], + ), + ], + ), + ], + Space, + ), + ), + ], + }, + @510-513 List( + [ + @511-512 Num( + "0", + ), + ], + ), + ], + Space, + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ), + [ + Newline, + ], + ), + @534-549 SpaceBefore( + RequiredValue( + @534-544 "siblingIds", + [], + @546-549 List( + [ + @547-548 Num( + "1", + ), + ], + ), + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + }, + ], + }, + @562-644 SpaceBefore( + BinOps( + [ + ( + @563-593 SpaceAfter( + ParensAround( + BinOps( + [ + ( + @563-575 RecordAccess( + Var { + module_name: "", + ident: "actual", + }, + "nodes", + ), + @576-578 Equals, + ), + ], + @579-593 RecordAccess( + Var { + module_name: "", + ident: "expected", + }, + "nodes", + ), + ), + ), + [ + Newline, + ], + ), + @599-601 And, + ), + ], + @603-643 ParensAround( + BinOps( + [ + ( + @603-620 RecordAccess( + Var { + module_name: "", + ident: "actual", + }, + "siblingIds", + ), + @621-623 Equals, + ), + ], + @624-643 RecordAccess( + Var { + module_name: "", + ident: "expected", + }, + "siblingIds", + ), + ), + ), + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + preceding_comment: …, + }, + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc new file mode 100644 index 00000000000..713be555b0d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc @@ -0,0 +1,20 @@ +expect + html : Html {} + html = + Element "a" 43 [HtmlAttr "href" "https://www.roc-lang.org/"] [Text "Roc"] + + actual : { nodes : List RenderedNode, siblingIds : List U64 } + actual = + indexNodes { nodes: [], siblingIds: [] } html + + expected : { nodes : List RenderedNode, siblingIds : List U64 } + expected = { + nodes: [ + RenderedText "Roc", + RenderedElement "a" { emptyRenderedAttrs & htmlAttrs: Dict.fromList [("href", "https://www.roc-lang.org/")] } [0], + ], + siblingIds: [1], + } + + (actual.nodes == expected.nodes) + && (actual.siblingIds == expected.siblingIds) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc deleted file mode 100644 index 0c0307e9783..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc +++ /dev/null @@ -1,2 +0,0 @@ -# expecting some effects -expect-fx 5 == 2 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast index 477eac6deb9..1e12f3ed39d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast @@ -9,12 +9,13 @@ Defs { Slice(start = 0, length = 1), ], space_after: [ - Slice(start = 1, length = 0), + Slice(start = 1, length = 1), ], spaces: [ LineComment( " expecting some effects", ), + Newline, ], type_defs: [], value_defs: [ @@ -32,7 +33,7 @@ Defs { "2", ), ), - preceding_comment: @25-25, + preceding_comment: @0-24, }, ], } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc new file mode 100644 index 00000000000..e87550b8058 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc @@ -0,0 +1,7 @@ +x = 5 + +expect x == y + +expect y == z + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast new file mode 100644 index 00000000000..80bf61088e2 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast @@ -0,0 +1,91 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @0-5, + @7-20, + @22-35, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", + }, + @4-5 Num( + "5", + ), + ), + Expect { + condition: @14-20 BinOps( + [ + ( + @14-15 Var { + module_name: "", + ident: "x", + }, + @16-18 Equals, + ), + ], + @19-20 Var { + module_name: "", + ident: "y", + }, + ), + preceding_comment: @7-7, + }, + Expect { + condition: @29-35 BinOps( + [ + ( + @29-30 Var { + module_name: "", + ident: "y", + }, + @31-33 Equals, + ), + ], + @34-35 Var { + module_name: "", + ident: "z", + }, + ), + preceding_comment: @22-22, + }, + ], + }, + @37-39 SpaceBefore( + Num( + "42", + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc new file mode 100644 index 00000000000..b90a629804f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc @@ -0,0 +1,7 @@ +x = 5 + +expect x == y + +expect y == z + +42 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc new file mode 100644 index 00000000000..51a94ceb1e9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc @@ -0,0 +1,6 @@ +if foo then + x = a # 1 + x # 2 +else + # 3 + c # 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast new file mode 100644 index 00000000000..b9aa239ccb2 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast @@ -0,0 +1,80 @@ +SpaceAfter( + If( + [ + ( + @3-6 Var { + module_name: "", + ident: "foo", + }, + @16-31 SpaceBefore( + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @16-21, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @16-17 Identifier { + ident: "x", + }, + @20-21 Var { + module_name: "", + ident: "a", + }, + ), + ], + }, + @30-31 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + LineComment( + " 1", + ), + ], + ), + ), + [ + LineComment( + " 2", + ), + ], + ), + [ + Newline, + ], + ), + ), + ], + @49-50 SpaceBefore( + Var { + module_name: "", + ident: "c", + }, + [ + LineComment( + " 3", + ), + ], + ), + ), + [ + LineComment( + " 4", + ), + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc new file mode 100644 index 00000000000..73f9076ea9a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc @@ -0,0 +1,5 @@ +if foo then + x = a # 1 + x # 2 +else # 3 + c # 4 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc new file mode 100644 index 00000000000..0f097ea6a8d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc @@ -0,0 +1,4 @@ +f : (Str)a -> (Str)a +f = \x -> x + +f ("Str", 42) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index 3f0d8f387e5..1d55e4203c0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -1,99 +1,104 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-32, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-1 Identifier { - ident: "f", - }, - ann_type: @4-20 Function( - [ - @4-10 Tuple { +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-32, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-1 Identifier { + ident: "f", + }, + ann_type: @4-20 Function( + [ + @4-10 Tuple { + elems: [ + @5-8 Apply( + "", + "Str", + [], + ), + ], + ext: Some( + @9-10 BoundVariable( + "a", + ), + ), + }, + ], + @14-20 Tuple { elems: [ - @5-8 Apply( + @15-18 Apply( "", "Str", [], ), ], ext: Some( - @9-10 BoundVariable( + @19-20 BoundVariable( "a", ), ), }, - ], - @14-20 Tuple { - elems: [ - @15-18 Apply( - "", - "Str", - [], - ), - ], - ext: Some( - @19-20 BoundVariable( - "a", - ), - ), + ), + comment: None, + body_pattern: @21-22 Identifier { + ident: "f", }, - ), - comment: None, - body_pattern: @21-22 Identifier { - ident: "f", - }, - body_expr: @25-32 Closure( - [ - @26-27 Identifier { + body_expr: @25-32 Closure( + [ + @26-27 Identifier { + ident: "x", + }, + ], + @31-32 Var { + module_name: "", ident: "x", }, - ], - @31-32 Var { - module_name: "", - ident: "x", - }, - ), - }, - ], - }, - @34-47 SpaceBefore( - Apply( - @34-35 Var { - module_name: "", - ident: "f", - }, - [ - @36-47 Tuple( - [ - @37-42 Str( - PlainLine( - "Str", + ), + }, + ], + }, + @34-47 SpaceBefore( + Apply( + @34-35 Var { + module_name: "", + ident: "f", + }, + [ + @36-47 Tuple( + [ + @37-42 Str( + PlainLine( + "Str", + ), ), - ), - @44-46 Num( - "42", - ), - ], - ), + @44-46 Num( + "42", + ), + ], + ), + ], + Space, + ), + [ + Newline, + Newline, ], - Space, ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc new file mode 100644 index 00000000000..98cad54ca6a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc @@ -0,0 +1,4 @@ +f : I64 -> (I64, I64) +f = \x -> (x, x + 1) + +f 42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index c150ac78960..cd1051ffb2e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -1,100 +1,105 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-42, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-1 Identifier { - ident: "f", - }, - ann_type: @4-21 Function( - [ - @4-7 Apply( - "", - "I64", - [], - ), - ], - @11-21 Tuple { - elems: [ - @12-15 Apply( - "", - "I64", - [], - ), - @17-20 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-42, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-1 Identifier { + ident: "f", + }, + ann_type: @4-21 Function( + [ + @4-7 Apply( "", "I64", [], ), ], - ext: None, - }, - ), - comment: None, - body_pattern: @22-23 Identifier { - ident: "f", - }, - body_expr: @26-42 Closure( - [ - @27-28 Identifier { - ident: "x", + @11-21 Tuple { + elems: [ + @12-15 Apply( + "", + "I64", + [], + ), + @17-20 Apply( + "", + "I64", + [], + ), + ], + ext: None, }, - ], - @32-42 Tuple( + ), + comment: None, + body_pattern: @22-23 Identifier { + ident: "f", + }, + body_expr: @26-42 Closure( [ - @33-34 Var { - module_name: "", + @27-28 Identifier { ident: "x", }, - @36-41 BinOps( - [ - ( - @36-37 Var { - module_name: "", - ident: "x", - }, - @38-39 Plus, + ], + @32-42 Tuple( + [ + @33-34 Var { + module_name: "", + ident: "x", + }, + @36-41 BinOps( + [ + ( + @36-37 Var { + module_name: "", + ident: "x", + }, + @38-39 Plus, + ), + ], + @40-41 Num( + "1", ), - ], - @40-41 Num( - "1", ), - ), - ], + ], + ), + ), + }, + ], + }, + @44-48 SpaceBefore( + Apply( + @44-45 Var { + module_name: "", + ident: "f", + }, + [ + @46-48 Num( + "42", ), - ), - }, - ], - }, - @44-48 SpaceBefore( - Apply( - @44-45 Var { - module_name: "", - ident: "f", - }, + ], + Space, + ), [ - @46-48 Num( - "42", - ), + Newline, + Newline, ], - Space, ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast index b5f729a47ad..b36dc45d82c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast @@ -1,37 +1,42 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-6, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "iffy", - }, - @5-6 Num( - "5", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-6, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-4 Identifier { + ident: "iffy", + }, + @5-6 Num( + "5", + ), ), + ], + }, + @8-10 SpaceBefore( + Num( + "42", ), - ], - }, - @8-10 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast index 8f808a2c9c7..5a6793dd0d2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ ModuleImport( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast index 148257a05ce..c8338c771c7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast @@ -13,20 +13,21 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - ], - space_after: [ Slice(start = 0, length = 1), Slice(start = 1, length = 1), Slice(start = 2, length = 1), - Slice(start = 3, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 1), ], spaces: [ Newline, Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast index d38b6a5171f..d2f47eda5c9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast @@ -9,14 +9,15 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), + Slice(start = 0, length = 1), ], space_after: [ - Slice(start = 0, length = 1), - Slice(start = 1, length = 0), + Slice(start = 0, length = 0), + Slice(start = 1, length = 1), ], spaces: [ Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast index 332bcb876e8..7d794b25e8a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast @@ -31,20 +31,6 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - Slice(start = 4, length = 0), - Slice(start = 6, length = 0), - Slice(start = 8, length = 0), - Slice(start = 10, length = 0), - Slice(start = 12, length = 0), - Slice(start = 14, length = 0), - Slice(start = 16, length = 0), - Slice(start = 18, length = 0), - Slice(start = 20, length = 0), - Slice(start = 23, length = 0), - Slice(start = 25, length = 0), - ], - space_after: [ Slice(start = 0, length = 2), Slice(start = 2, length = 2), Slice(start = 4, length = 2), @@ -57,7 +43,21 @@ Defs { Slice(start = 18, length = 2), Slice(start = 20, length = 3), Slice(start = 23, length = 2), - Slice(start = 25, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + Slice(start = 6, length = 0), + Slice(start = 8, length = 0), + Slice(start = 10, length = 0), + Slice(start = 12, length = 0), + Slice(start = 14, length = 0), + Slice(start = 16, length = 0), + Slice(start = 18, length = 0), + Slice(start = 20, length = 0), + Slice(start = 23, length = 0), + Slice(start = 25, length = 1), ], spaces: [ Newline, @@ -87,6 +87,7 @@ Defs { LineComment( " comment between imports", ), + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast index 27e3b2ca5e8..cdf8ee10509 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast @@ -11,17 +11,18 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - ], - space_after: [ Slice(start = 0, length = 1), Slice(start = 1, length = 1), - Slice(start = 2, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast index d3dd6f81cbb..94c9cf71372 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast @@ -13,20 +13,21 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - ], - space_after: [ Slice(start = 0, length = 1), Slice(start = 1, length = 1), Slice(start = 2, length = 1), - Slice(start = 3, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 1), ], spaces: [ Newline, Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc new file mode 100644 index 00000000000..9ccdd8a175b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc @@ -0,0 +1,6 @@ +fillBucketsFromData = \buckets0, data, shifts -> + buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0 + (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts + placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex + +foo \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast new file mode 100644 index 00000000000..59e7f66a64f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast @@ -0,0 +1,196 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-288, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-19 Identifier { + ident: "fillBucketsFromData", + }, + @22-288 Closure( + [ + @23-31 Identifier { + ident: "buckets0", + }, + @33-37 Identifier { + ident: "data", + }, + @39-45 Identifier { + ident: "shifts", + }, + ], + @53-288 SpaceBefore( + Backpassing( + [ + @53-61 Identifier { + ident: "buckets1", + }, + @63-71 Tuple( + [ + @64-67 Identifier { + ident: "key", + }, + @69-70 Underscore( + "", + ), + ], + ), + @73-82 Identifier { + ident: "dataIndex", + }, + ], + @86-118 Apply( + @86-104 Var { + module_name: "List", + ident: "walkWithIndex", + }, + [ + @105-109 Var { + module_name: "", + ident: "data", + }, + @110-118 Var { + module_name: "", + ident: "buckets0", + }, + ], + Space, + ), + @123-288 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @123-192, + ], + space_before: [ + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @123-156 Tuple( + [ + @124-135 Identifier { + ident: "bucketIndex", + }, + @137-155 Identifier { + ident: "distAndFingerprint", + }, + ], + ), + @159-192 Apply( + @159-172 Var { + module_name: "", + ident: "nextWhileLess", + }, + [ + @173-181 Var { + module_name: "", + ident: "buckets1", + }, + @182-185 Var { + module_name: "", + ident: "key", + }, + @186-192 Var { + module_name: "", + ident: "shifts", + }, + ], + Space, + ), + ), + ], + }, + @197-288 SpaceBefore( + Apply( + @197-212 Var { + module_name: "", + ident: "placeAndShiftUp", + }, + [ + @213-221 Var { + module_name: "", + ident: "buckets1", + }, + @222-276 Record( + [ + @224-242 LabelOnly( + @224-242 "distAndFingerprint", + ), + @244-274 RequiredValue( + @244-253 "dataIndex", + [], + @255-274 Apply( + @255-264 Var { + module_name: "Num", + ident: "toU32", + }, + [ + @265-274 Var { + module_name: "", + ident: "dataIndex", + }, + ], + Space, + ), + ), + ], + ), + @277-288 Var { + module_name: "", + ident: "bucketIndex", + }, + ], + Space, + ), + [ + Newline, + ], + ), + ), + ), + [ + Newline, + ], + ), + ), + ), + ], + }, + @290-293 SpaceBefore( + Var { + module_name: "", + ident: "foo", + }, + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc new file mode 100644 index 00000000000..c1bac9b6141 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc @@ -0,0 +1,6 @@ +fillBucketsFromData = \buckets0, data, shifts -> + buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0 + (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts + placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex + +foo diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast index b15064f593e..f14ad2b55bc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast @@ -9,14 +9,15 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), + Slice(start = 0, length = 1), ], space_after: [ - Slice(start = 0, length = 1), - Slice(start = 1, length = 0), + Slice(start = 0, length = 0), + Slice(start = 1, length = 1), ], spaces: [ Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc new file mode 100644 index 00000000000..9289474d2c8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc @@ -0,0 +1,4 @@ +import Json exposing [int] +import Json.Encode as JE + +JE.encode (int 42) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast index 8e58215fa3d..4a34bd020f4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast @@ -1,103 +1,112 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @0-26, - @27-51, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - space_after: [ - Slice(start = 0, length = 1), - Slice(start = 1, length = 2), - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - ModuleImport( - ModuleImport { - before_name: [], - name: @7-11 ImportedModuleName { - package: None, - name: ModuleName( - "Json", - ), - }, - params: None, - alias: None, - exposed: Some( - KeywordItem { - keyword: Spaces { - before: [], - item: ImportExposingKeyword, - after: [], - }, - item: [ - @22-25 ExposedName( - "int", - ), - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @0-26, + @27-51, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + ModuleImport( + ModuleImport { + before_name: [], + name: @7-11 ImportedModuleName { + package: None, + name: ModuleName( + "Json", + ), }, - ), - }, - ), - ModuleImport( - ModuleImport { - before_name: [], - name: @34-45 ImportedModuleName { - package: None, - name: ModuleName( - "Json.Encode", + params: None, + alias: None, + exposed: Some( + KeywordItem { + keyword: Spaces { + before: [], + item: ImportExposingKeyword, + after: [], + }, + item: [ + @22-25 ExposedName( + "int", + ), + ], + }, ), }, - params: None, - alias: Some( - KeywordItem { - keyword: Spaces { - before: [], - item: ImportAsKeyword, - after: [], - }, - item: @49-51 ImportAlias( - "JE", + ), + ModuleImport( + ModuleImport { + before_name: [], + name: @34-45 ImportedModuleName { + package: None, + name: ModuleName( + "Json.Encode", ), }, - ), - exposed: None, - }, - ), - ], - }, - @53-71 Apply( - @53-62 Var { - module_name: "JE", - ident: "encode", - }, - [ - @64-70 ParensAround( - Apply( - @64-67 Var { - module_name: "", - ident: "int", - }, - [ - @68-70 Num( - "42", + params: None, + alias: Some( + KeywordItem { + keyword: Spaces { + before: [], + item: ImportAsKeyword, + after: [], + }, + item: @49-51 ImportAlias( + "JE", + ), + }, ), - ], - Space, + exposed: None, + }, ), + ], + }, + @53-71 SpaceBefore( + Apply( + @53-62 Var { + module_name: "JE", + ident: "encode", + }, + [ + @64-70 ParensAround( + Apply( + @64-67 Var { + module_name: "", + ident: "int", + }, + [ + @68-70 Num( + "42", + ), + ], + Space, + ), + ), + ], + Space, ), - ], - Space, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc new file mode 100644 index 00000000000..743aa4e3b7d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc @@ -0,0 +1,3 @@ +import "users.json" as data : Str + +parseJson data \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast index c71b523d09d..7323b043f44 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast @@ -1,62 +1,70 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-33, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 2), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - IngestedFileImport( - IngestedFileImport { - before_path: [], - path: @7-19 PlainLine( - "users.json", - ), - name: KeywordItem { - keyword: Spaces { - before: [], - item: ImportAsKeyword, - after: [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-33, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + IngestedFileImport( + IngestedFileImport { + before_path: [], + path: @7-19 PlainLine( + "users.json", + ), + name: KeywordItem { + keyword: Spaces { + before: [], + item: ImportAsKeyword, + after: [], + }, + item: @23-27 "data", }, - item: @23-27 "data", + annotation: Some( + IngestedFileAnnotation { + before_colon: [], + annotation: @30-33 Apply( + "", + "Str", + [], + ), + }, + ), }, - annotation: Some( - IngestedFileAnnotation { - before_colon: [], - annotation: @30-33 Apply( - "", - "Str", - [], - ), - }, - ), + ), + ], + }, + @35-49 SpaceBefore( + Apply( + @35-44 Var { + module_name: "", + ident: "parseJson", }, + [ + @45-49 Var { + module_name: "", + ident: "data", + }, + ], + Space, ), - ], - }, - @35-49 Apply( - @35-44 Var { - module_name: "", - ident: "parseJson", - }, - [ - @45-49 Var { - module_name: "", - ident: "data", - }, - ], - Space, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc new file mode 100644 index 00000000000..39a00366076 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc @@ -0,0 +1,3 @@ +import "users.json" as data + +parseJson data \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast index 8a91304d12e..00a09fe4679 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast @@ -1,53 +1,61 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-27, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 2), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - IngestedFileImport( - IngestedFileImport { - before_path: [], - path: @7-19 PlainLine( - "users.json", - ), - name: KeywordItem { - keyword: Spaces { - before: [], - item: ImportAsKeyword, - after: [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-27, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + IngestedFileImport( + IngestedFileImport { + before_path: [], + path: @7-19 PlainLine( + "users.json", + ), + name: KeywordItem { + keyword: Spaces { + before: [], + item: ImportAsKeyword, + after: [], + }, + item: @23-27 "data", }, - item: @23-27 "data", + annotation: None, }, - annotation: None, + ), + ], + }, + @29-43 SpaceBefore( + Apply( + @29-38 Var { + module_name: "", + ident: "parseJson", }, + [ + @39-43 Var { + module_name: "", + ident: "data", + }, + ], + Space, ), - ], - }, - @29-43 Apply( - @29-38 Var { - module_name: "", - ident: "parseJson", - }, - [ - @39-43 Var { - module_name: "", - ident: "data", - }, - ], - Space, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast index 48f73696b8d..85c58d43173 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast @@ -1,90 +1,95 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-57, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Identifier { - ident: "myList", - }, - @9-57 List( - Collection { - items: [ - @15-16 SpaceBefore( - Num( - "0", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-57, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-6 Identifier { + ident: "myList", + }, + @9-57 List( + Collection { + items: [ + @15-16 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @22-47 SpaceBefore( - List( - Collection { - items: [ - @32-33 SpaceBefore( - Var { - module_name: "", - ident: "a", - }, - [ - Newline, - ], - ), - @43-44 SpaceBefore( - Var { - module_name: "", - ident: "b", - }, - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, + @22-47 SpaceBefore( + List( + Collection { + items: [ + @32-33 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + ], + ), + @43-44 SpaceBefore( + Var { + module_name: "", + ident: "b", + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @53-54 SpaceBefore( - Num( - "1", + @53-54 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, + ], + final_comments: [ + Newline, + ], + }, + ), ), + ], + }, + @58-60 SpaceBefore( + Num( + "42", ), - ], - }, - @58-60 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast index f9f33d2f02c..867438c3385 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast @@ -1,58 +1,63 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-25, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Identifier { - ident: "myList", - }, - @9-25 List( - [ - @15-16 SpaceBefore( - Num( - "0", - ), - [ - Newline, - ], - ), - @22-23 SpaceBefore( - SpaceAfter( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-25, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-6 Identifier { + ident: "myList", + }, + @9-25 List( + [ + @15-16 SpaceBefore( Num( - "1", + "0", + ), + [ + Newline, + ], + ), + @22-23 SpaceBefore( + SpaceAfter( + Num( + "1", + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], + ], + ), ), + ], + }, + @26-28 SpaceBefore( + Num( + "42", ), - ], - }, - @26-28 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc new file mode 100644 index 00000000000..5a937217c94 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc @@ -0,0 +1,5 @@ +myList = [ + 0, + 1, +] +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast index 622b1b9d9d6..99d07201abf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast @@ -1,58 +1,63 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Identifier { - ident: "myList", - }, - @9-26 List( - Collection { - items: [ - @15-16 SpaceBefore( - Num( - "0", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-26, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-6 Identifier { + ident: "myList", + }, + @9-26 List( + Collection { + items: [ + @15-16 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @22-23 SpaceBefore( - Num( - "1", + @22-23 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, + ], + final_comments: [ + Newline, + ], + }, + ), ), + ], + }, + @27-29 SpaceBefore( + Num( + "42", ), - ], - }, - @27-29 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast index 25dfb5a65a4..11c170d5459 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-7 List( - [], - ), - [ - WhenBranch { - patterns: [ - @15-24 SpaceBefore( - List( - [ - @16-17 NumLiteral( - "1", - ), - @19-20 NumLiteral( - "2", - ), - @22-23 SpaceBefore( - NumLiteral( - "3", +SpaceAfter( + When( + @5-7 List( + [], + ), + [ + WhenBranch { + patterns: [ + @15-24 SpaceBefore( + List( + [ + @16-17 NumLiteral( + "1", + ), + @19-20 NumLiteral( + "2", + ), + @22-23 SpaceBefore( + NumLiteral( + "3", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), + ], + ), + [ + Newline, ], ), - [ - Newline, - ], - ), - ], - value: @28-30 Str( - PlainLine( - "", + ], + value: @28-30 Str( + PlainLine( + "", + ), ), - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast index 99f74fc8cb5..5d31966b51e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast @@ -1,228 +1,233 @@ -When( - @5-7 List( - [], - ), - [ - WhenBranch { - patterns: [ - @13-15 SpaceBefore( - List( - [], +SpaceAfter( + When( + @5-7 List( + [], + ), + [ + WhenBranch { + patterns: [ + @13-15 SpaceBefore( + List( + [], + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @19-21 Record( + [], ), - ], - value: @19-21 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @24-28 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @24-28 SpaceBefore( + List( + [ + @25-27 ListRest( + None, + ), + ], + ), [ - @25-27 ListRest( - None, - ), + Newline, ], ), - [ - Newline, - ], + ], + value: @32-34 Record( + [], ), - ], - value: @32-34 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @37-51 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @37-51 SpaceBefore( + List( + [ + @38-39 Underscore( + "", + ), + @41-43 ListRest( + None, + ), + @45-46 Underscore( + "", + ), + @48-50 ListRest( + None, + ), + ], + ), [ - @38-39 Underscore( - "", - ), - @41-43 ListRest( - None, - ), - @45-46 Underscore( - "", - ), - @48-50 ListRest( - None, - ), + Newline, ], ), - [ - Newline, - ], + ], + value: @55-57 Record( + [], ), - ], - value: @55-57 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @60-72 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @60-72 SpaceBefore( + List( + [ + @61-62 Identifier { + ident: "a", + }, + @64-65 Identifier { + ident: "b", + }, + @67-68 Identifier { + ident: "c", + }, + @70-71 Identifier { + ident: "d", + }, + ], + ), [ - @61-62 Identifier { - ident: "a", - }, - @64-65 Identifier { - ident: "b", - }, - @67-68 Identifier { - ident: "c", - }, - @70-71 Identifier { - ident: "d", - }, + Newline, ], ), - [ - Newline, - ], + ], + value: @76-78 Record( + [], ), - ], - value: @76-78 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @81-91 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @81-91 SpaceBefore( + List( + [ + @82-83 Identifier { + ident: "a", + }, + @85-86 Identifier { + ident: "b", + }, + @88-90 ListRest( + None, + ), + ], + ), [ - @82-83 Identifier { - ident: "a", - }, - @85-86 Identifier { - ident: "b", - }, - @88-90 ListRest( - None, - ), + Newline, ], ), - [ - Newline, - ], + ], + value: @95-97 Record( + [], ), - ], - value: @95-97 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @100-110 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @100-110 SpaceBefore( + List( + [ + @101-103 ListRest( + None, + ), + @105-106 Identifier { + ident: "c", + }, + @108-109 Identifier { + ident: "d", + }, + ], + ), [ - @101-103 ListRest( - None, - ), - @105-106 Identifier { - ident: "c", - }, - @108-109 Identifier { - ident: "d", - }, + Newline, ], ), - [ - Newline, - ], + ], + value: @114-116 Record( + [], ), - ], - value: @114-116 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @119-135 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @119-135 SpaceBefore( + List( + [ + @120-123 List( + [ + @121-122 Tag( + "A", + ), + ], + ), + @125-129 List( + [ + @126-128 ListRest( + None, + ), + ], + ), + @131-134 List( + [ + @132-133 Identifier { + ident: "a", + }, + ], + ), + ], + ), [ - @120-123 List( - [ - @121-122 Tag( - "A", - ), - ], - ), - @125-129 List( - [ - @126-128 ListRest( - None, - ), - ], - ), - @131-134 List( - [ - @132-133 Identifier { - ident: "a", - }, - ], - ), + Newline, ], ), - [ - Newline, - ], + ], + value: @139-141 Record( + [], ), - ], - value: @139-141 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @144-163 SpaceBefore( - List( + guard: None, + }, + WhenBranch { + patterns: [ + @144-163 SpaceBefore( + List( + [ + @145-153 List( + [ + @146-148 List( + [], + ), + @150-152 List( + [], + ), + ], + ), + @155-162 List( + [ + @156-158 List( + [], + ), + @160-161 Identifier { + ident: "x", + }, + ], + ), + ], + ), [ - @145-153 List( - [ - @146-148 List( - [], - ), - @150-152 List( - [], - ), - ], - ), - @155-162 List( - [ - @156-158 List( - [], - ), - @160-161 Identifier { - ident: "x", - }, - ], - ), + Newline, ], ), - [ - Newline, - ], + ], + value: @167-169 Record( + [], ), - ], - value: @167-169 Record( - [], - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc deleted file mode 100644 index 1ee25711f7f..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -app [] {} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc new file mode 100644 index 00000000000..0160e797bab --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc @@ -0,0 +1,7 @@ +### not docs! +## docs, but with a problem +## (namely that this is a mix of docs and regular comments) +# not docs +x = 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast index ec6f2288f35..0179f6a3cdf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @113-118, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @113-114 Identifier { - ident: "x", - }, - @117-118 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @113-118, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @113-114 Identifier { + ident: "x", + }, + @117-118 Num( + "5", + ), ), + ], + }, + @120-122 SpaceBefore( + Num( + "42", ), - ], - }, - @120-122 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc deleted file mode 100644 index c14cf34253b..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -main = - i = 64 - - i diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast index e333a087707..5c8706f54b5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc deleted file mode 100644 index 9fd98c5b23f..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -module [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc new file mode 100644 index 00000000000..d4c0fbdec34 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc @@ -0,0 +1,3 @@ +x, y <- List.map2 [] [] + +x + y \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast index ba2df50c34a..9b330855152 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast @@ -1,46 +1,51 @@ -Backpassing( - [ - @0-1 Identifier { - ident: "x", - }, - @3-4 Identifier { - ident: "y", - }, - ], - @8-23 Apply( - @8-17 Var { - module_name: "List", - ident: "map2", - }, +SpaceAfter( + Backpassing( [ - @18-20 List( - [], - ), - @21-23 List( - [], - ), + @0-1 Identifier { + ident: "x", + }, + @3-4 Identifier { + ident: "y", + }, ], - Space, - ), - @25-30 SpaceBefore( - BinOps( + @8-23 Apply( + @8-17 Var { + module_name: "List", + ident: "map2", + }, [ - ( - @25-26 Var { - module_name: "", - ident: "x", - }, - @27-28 Plus, + @18-20 List( + [], + ), + @21-23 List( + [], ), ], - @29-30 Var { - module_name: "", - ident: "y", - }, + Space, + ), + @25-30 SpaceBefore( + BinOps( + [ + ( + @25-26 Var { + module_name: "", + ident: "x", + }, + @27-28 Plus, + ), + ], + @29-30 Var { + module_name: "", + ident: "y", + }, + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast index a94ee987454..5249cd6a171 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc new file mode 100644 index 00000000000..7e9668e7834 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc @@ -0,0 +1 @@ +"foo" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast index 21f0b7d0a6e..a090b1b3536 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast @@ -1,5 +1,10 @@ -Str( - PlainLine( - "foo", +SpaceAfter( + Str( + PlainLine( + "foo", + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc new file mode 100644 index 00000000000..057218582ab --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc @@ -0,0 +1,25 @@ +when + x + + 1 # comment 1 + > 0 # comment 2 +is + y -> + 3 + * 2 # comment 3 + < 1 # comment 4 + + z -> + 4 + / 5 # comment 5 + < 1 # comment 6 + + 46 # first pattern comment + | 95 # alternative comment 1 + | 126 # alternative comment 2 + | 150 -> # This comment came after the -> + # This comment is for the expr + foo bar + |> Result.withDefault "" # one last comment + + _ -> + 42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast new file mode 100644 index 00000000000..4827eabd522 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast @@ -0,0 +1,268 @@ +When( + @9-38 SpaceBefore( + SpaceAfter( + BinOps( + [ + ( + @9-10 SpaceAfter( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), + @15-16 Plus, + ), + ( + @17-18 SpaceAfter( + Num( + "1", + ), + [ + LineComment( + " comment 1", + ), + ], + ), + @35-36 GreaterThan, + ), + ], + @37-38 Num( + "0", + ), + ), + [ + LineComment( + " comment 2", + ), + ], + ), + [ + Newline, + ], + ), + [ + WhenBranch { + patterns: [ + @58-59 SpaceBefore( + Identifier { + ident: "y", + }, + [ + Newline, + ], + ), + ], + value: @71-108 SpaceBefore( + BinOps( + [ + ( + @71-72 SpaceAfter( + Num( + "3", + ), + [ + Newline, + ], + ), + @81-82 Star, + ), + ( + @83-84 SpaceAfter( + Num( + "2", + ), + [ + LineComment( + " comment 3", + ), + ], + ), + @105-106 LessThan, + ), + ], + @107-108 Num( + "1", + ), + ), + [ + Newline, + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @126-127 SpaceBefore( + Identifier { + ident: "z", + }, + [ + LineComment( + " comment 4", + ), + Newline, + ], + ), + ], + value: @139-189 SpaceBefore( + BinOps( + [ + ( + @139-140 SpaceAfter( + Num( + "4", + ), + [ + Newline, + ], + ), + @153-154 Slash, + ), + ( + @155-156 SpaceAfter( + Num( + "5", + ), + [ + LineComment( + " comment 5", + ), + ], + ), + @186-187 LessThan, + ), + ], + @188-189 Num( + "1", + ), + ), + [ + Newline, + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @210-212 SpaceBefore( + SpaceAfter( + NumLiteral( + "46", + ), + [ + LineComment( + " first pattern comment", + ), + ], + ), + [ + LineComment( + " comment 6", + ), + Newline, + ], + ), + @243-245 SpaceAfter( + NumLiteral( + "95", + ), + [ + LineComment( + " alternative comment 1", + ), + ], + ), + @276-279 SpaceAfter( + NumLiteral( + "126", + ), + [ + LineComment( + " alternative comment 2", + ), + ], + ), + @310-313 NumLiteral( + "150", + ), + ], + value: @401-449 SpaceBefore( + BinOps( + [ + ( + @401-408 SpaceAfter( + Apply( + @401-404 Var { + module_name: "", + ident: "foo", + }, + [ + @405-408 Var { + module_name: "", + ident: "bar", + }, + ], + Space, + ), + [ + Newline, + ], + ), + @425-427 Pizza, + ), + ], + @428-449 Apply( + @428-446 Var { + module_name: "Result", + ident: "withDefault", + }, + [ + @447-449 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + [ + LineComment( + " This comment came after the ->", + ), + LineComment( + " This comment is for the expr", + ), + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @474-475 SpaceBefore( + Underscore( + "", + ), + [ + LineComment( + " one last comment", + ), + Newline, + ], + ), + ], + value: @487-489 SpaceBefore( + Num( + "42", + ), + [ + Newline, + ], + ), + guard: None, + }, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc new file mode 100644 index 00000000000..1ae16e1f185 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc @@ -0,0 +1,25 @@ +when + x + + 1 # comment 1 + > 0 # comment 2 +is + y -> + 3 + * 2 # comment 3 + < 1 # comment 4 + + z -> + 4 + / 5 # comment 5 + < 1 # comment 6 + + 46 # first pattern comment + | 95 # alternative comment 1 + | 126 # alternative comment 2 + | 150 -> # This comment came after the -> + # This comment is for the expr + foo bar + |> Result.withDefault "" # one last comment + + _ -> + 42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc new file mode 100644 index 00000000000..be56926ac86 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc @@ -0,0 +1,4 @@ +1 + """ + """ "^" 2 : A +"" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast new file mode 100644 index 00000000000..3cd900154f9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast @@ -0,0 +1,62 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-13, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Apply( + @0-1 NumLiteral( + "1", + ), + [ + @1-7 StrLiteral( + Block( + [], + ), + ), + @7-10 StrLiteral( + PlainLine( + "^", + ), + ), + @10-11 NumLiteral( + "2", + ), + ], + ), + @12-13 Apply( + "", + "A", + [], + ), + ), + ], + }, + @14-16 SpaceBefore( + Str( + PlainLine( + "", + ), + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc new file mode 100644 index 00000000000..59d9b1d8ee3 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc @@ -0,0 +1,2 @@ +1"""""""^"2:A +"" diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast index 708bdb8a46e..a29131e3202 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast @@ -1,61 +1,38 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), - ], - regions: [ - @0-22, - @23-49, - @50-92, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "a", - }, - @4-22 Str( - Line( - [ - Plaintext( - "Hello,", - ), - EscapedChar( - Newline, - ), - EscapedChar( - Newline, - ), - Plaintext( - "World!", - ), - ], - ), - ), - ), - Body( - @23-24 Identifier { - ident: "b", - }, - @27-49 Str( - Block( - [ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @0-22, + @23-49, + @50-92, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "a", + }, + @4-22 Str( + Line( [ Plaintext( "Hello,", @@ -70,24 +47,25 @@ Defs( "World!", ), ], - ], + ), ), ), - ), - Body( - @50-51 Identifier { - ident: "c", - }, - @58-92 SpaceBefore( - Str( + Body( + @23-24 Identifier { + ident: "b", + }, + @27-49 Str( Block( [ [ Plaintext( - "Hello,\n", + "Hello,", ), - Plaintext( - "\n", + EscapedChar( + Newline, + ), + EscapedChar( + Newline, ), Plaintext( "World!", @@ -96,19 +74,46 @@ Defs( ], ), ), - [ - Newline, - ], ), + Body( + @50-51 Identifier { + ident: "c", + }, + @58-92 SpaceBefore( + Str( + Block( + [ + [ + Plaintext( + "Hello,\n", + ), + Plaintext( + "\n", + ), + Plaintext( + "World!", + ), + ], + ], + ), + ), + [ + Newline, + ], + ), + ), + ], + }, + @93-95 SpaceBefore( + Num( + "42", ), - ], - }, - @93-95 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc deleted file mode 100644 index 28f581a04da..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc +++ /dev/null @@ -1,7 +0,0 @@ -main = - task = - file <- - foo - bar - task -42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast deleted file mode 100644 index ace33dc1fec..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast +++ /dev/null @@ -1,97 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-71, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-71 SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @11-62, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-15 Identifier { - ident: "task", - }, - @18-62 Backpassing( - [ - @18-22 Identifier { - ident: "file", - }, - ], - @43-46 SpaceBefore( - Var { - module_name: "", - ident: "foo", - }, - [ - Newline, - ], - ), - @59-62 SpaceBefore( - Var { - module_name: "", - ident: "bar", - }, - [ - Newline, - ], - ), - ), - ), - ], - }, - @67-71 SpaceBefore( - Var { - module_name: "", - ident: "task", - }, - [ - Newline, - ], - ), - ), - [ - Newline, - ], - ), - ), - ], - }, - @72-74 SpaceBefore( - Num( - "42", - ), - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc deleted file mode 100644 index d5ced4d9f7d..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc +++ /dev/null @@ -1,6 +0,0 @@ -main = - task = file <- - foo - bar - task -42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc deleted file mode 100644 index a7d12703ae0..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc +++ /dev/null @@ -1,6 +0,0 @@ -main = - wrappedNotEq : a, a -> Bool - wrappedNotEq = \num1, num2 -> - num1 != num2 - - wrappedNotEq 2 3 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index fe6b7bbee43..df05b75201d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc deleted file mode 100644 index 1f29cd8a05e..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -x = - a : n - 4 -_ \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast deleted file mode 100644 index c0cf621ea60..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast +++ /dev/null @@ -1,64 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-7, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @2-7 Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @2-5, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @2-3 Identifier { - ident: "a", - }, - @4-5 BoundVariable( - "n", - ), - ), - ], - }, - @6-7 Num( - "4", - ), - ), - ), - ], - }, - @8-9 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc deleted file mode 100644 index 35709dbea4c..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc +++ /dev/null @@ -1,2 +0,0 @@ -x=a:n 4 -_ \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast index c4542c48eb6..a393541c92e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast @@ -1,50 +1,55 @@ -If( - [ - ( - @3-5 Var { - module_name: "", - ident: "t1", - }, - @13-14 SpaceBefore( - SpaceAfter( - Num( - "1", +SpaceAfter( + If( + [ + ( + @3-5 Var { + module_name: "", + ident: "t1", + }, + @13-14 SpaceBefore( + SpaceAfter( + Num( + "1", + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], ), - ), - ( - @23-25 Var { - module_name: "", - ident: "t2", - }, - @33-34 SpaceBefore( - SpaceAfter( - Num( - "2", + ( + @23-25 Var { + module_name: "", + ident: "t2", + }, + @33-34 SpaceBefore( + SpaceAfter( + Num( + "2", + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], ), - ), - ], - @42-43 SpaceBefore( - Num( - "3", - ), - [ - Newline, ], + @42-43 SpaceBefore( + Num( + "3", + ), + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc new file mode 100644 index 00000000000..efd76014927 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc @@ -0,0 +1,4 @@ +x = + 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast index 87a156b4954..2a7da5e6640 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast @@ -1,42 +1,47 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-9, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @8-9 SpaceBefore( - Num( - "5", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-9, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", + }, + @8-9 SpaceBefore( + Num( + "5", + ), + [ + Newline, + ], ), - [ - Newline, - ], ), + ], + }, + @11-13 SpaceBefore( + Num( + "42", ), - ], - }, - @11-13 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast deleted file mode 100644 index 4a401f61bb0..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast +++ /dev/null @@ -1,49 +0,0 @@ -BinOps( - [ - ( - @0-1 SpaceAfter( - Num( - "7", - ), - [ - Newline, - ], - ), - @2-4 Equals, - ), - ], - @5-11 ParensAround( - Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @5-8, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Alias { - header: TypeHeader { - name: @5-6 "Q", - vars: [], - }, - ann: @7-8 BoundVariable( - "c", - ), - }, - ], - value_defs: [], - }, - @9-11 Num( - "42", - ), - ), - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast index 07e8f8fc19d..616fe065f8e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast @@ -43,11 +43,12 @@ Full { Slice(start = 0, length = 2), ], space_after: [ - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast index 9fafa599ed8..b3e603bc2da 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast @@ -1,413 +1,418 @@ -Record( - Collection { - items: [ - @4-15 SpaceBefore( - RequiredValue( - @4-6 "u8", - [], - @10-15 Num( - "123u8", - ), - ), - [ - Newline, - ], - ), - @19-31 SpaceBefore( - RequiredValue( - @19-22 "u16", - [], - @25-31 Num( - "123u16", - ), - ), - [ - Newline, - ], - ), - @35-47 SpaceBefore( - RequiredValue( - @35-38 "u32", - [], - @41-47 Num( - "123u32", - ), - ), - [ - Newline, - ], - ), - @51-63 SpaceBefore( - RequiredValue( - @51-54 "u64", - [], - @57-63 Num( - "123u64", - ), - ), - [ - Newline, - ], - ), - @67-80 SpaceBefore( - RequiredValue( - @67-71 "u128", - [], - @73-80 Num( - "123u128", - ), - ), - [ - Newline, - ], - ), - @84-95 SpaceBefore( - RequiredValue( - @84-86 "i8", - [], - @90-95 Num( - "123i8", - ), - ), - [ - Newline, - ], - ), - @99-111 SpaceBefore( - RequiredValue( - @99-102 "i16", - [], - @105-111 Num( - "123i16", - ), - ), - [ - Newline, - ], - ), - @115-127 SpaceBefore( - RequiredValue( - @115-118 "i32", - [], - @121-127 Num( - "123i32", - ), - ), - [ - Newline, - ], - ), - @131-143 SpaceBefore( - RequiredValue( - @131-134 "i64", - [], - @137-143 Num( - "123i64", - ), - ), - [ - Newline, - ], - ), - @147-160 SpaceBefore( - RequiredValue( - @147-151 "i128", - [], - @153-160 Num( - "123i128", - ), - ), - [ - Newline, - ], - ), - @164-176 SpaceBefore( - RequiredValue( - @164-167 "dec", - [], - @170-176 Num( - "123dec", - ), - ), - [ - Newline, - ], - ), - @180-195 SpaceBefore( - RequiredValue( - @180-185 "u8Neg", - [], - @189-195 Num( - "-123u8", - ), - ), - [ - Newline, - ], - ), - @199-215 SpaceBefore( - RequiredValue( - @199-205 "u16Neg", - [], - @208-215 Num( - "-123u16", - ), - ), - [ - Newline, - ], - ), - @219-235 SpaceBefore( - RequiredValue( - @219-225 "u32Neg", - [], - @228-235 Num( - "-123u32", - ), - ), - [ - Newline, - ], - ), - @239-255 SpaceBefore( - RequiredValue( - @239-245 "u64Neg", - [], - @248-255 Num( - "-123u64", - ), - ), - [ - Newline, - ], - ), - @259-276 SpaceBefore( - RequiredValue( - @259-266 "u128Neg", - [], - @268-276 Num( - "-123u128", - ), - ), - [ - Newline, - ], - ), - @280-295 SpaceBefore( - RequiredValue( - @280-285 "i8Neg", - [], - @289-295 Num( - "-123i8", - ), - ), - [ - Newline, - ], - ), - @299-315 SpaceBefore( - RequiredValue( - @299-305 "i16Neg", - [], - @308-315 Num( - "-123i16", - ), - ), - [ - Newline, - ], - ), - @319-335 SpaceBefore( - RequiredValue( - @319-325 "i32Neg", - [], - @328-335 Num( - "-123i32", - ), - ), - [ - Newline, - ], - ), - @339-355 SpaceBefore( - RequiredValue( - @339-345 "i64Neg", - [], - @348-355 Num( - "-123i64", - ), - ), - [ - Newline, - ], - ), - @359-376 SpaceBefore( - RequiredValue( - @359-366 "i128Neg", - [], - @368-376 Num( - "-123i128", - ), - ), - [ - Newline, - ], - ), - @380-396 SpaceBefore( - RequiredValue( - @380-386 "decNeg", - [], - @389-396 Num( - "-123dec", - ), - ), - [ - Newline, - ], - ), - @400-416 SpaceBefore( - RequiredValue( - @400-405 "u8Bin", - [], - @409-416 NonBase10Int { - string: "101u8", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @420-437 SpaceBefore( - RequiredValue( - @420-426 "u16Bin", - [], - @429-437 NonBase10Int { - string: "101u16", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @441-458 SpaceBefore( - RequiredValue( - @441-447 "u32Bin", - [], - @450-458 NonBase10Int { - string: "101u32", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @462-479 SpaceBefore( - RequiredValue( - @462-468 "u64Bin", - [], - @471-479 NonBase10Int { - string: "101u64", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @483-501 SpaceBefore( - RequiredValue( - @483-490 "u128Bin", - [], - @492-501 NonBase10Int { - string: "101u128", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @505-521 SpaceBefore( - RequiredValue( - @505-510 "i8Bin", - [], - @514-521 NonBase10Int { - string: "101i8", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @525-542 SpaceBefore( - RequiredValue( - @525-531 "i16Bin", - [], - @534-542 NonBase10Int { - string: "101i16", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @546-563 SpaceBefore( - RequiredValue( - @546-552 "i32Bin", - [], - @555-563 NonBase10Int { - string: "101i32", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @567-584 SpaceBefore( - RequiredValue( - @567-573 "i64Bin", - [], - @576-584 NonBase10Int { - string: "101i64", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - @588-606 SpaceBefore( - RequiredValue( - @588-595 "i128Bin", - [], - @597-606 NonBase10Int { - string: "101i128", - base: Binary, - is_negative: false, - }, - ), - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, +SpaceAfter( + Record( + Collection { + items: [ + @4-15 SpaceBefore( + RequiredValue( + @4-6 "u8", + [], + @10-15 Num( + "123u8", + ), + ), + [ + Newline, + ], + ), + @19-31 SpaceBefore( + RequiredValue( + @19-22 "u16", + [], + @25-31 Num( + "123u16", + ), + ), + [ + Newline, + ], + ), + @35-47 SpaceBefore( + RequiredValue( + @35-38 "u32", + [], + @41-47 Num( + "123u32", + ), + ), + [ + Newline, + ], + ), + @51-63 SpaceBefore( + RequiredValue( + @51-54 "u64", + [], + @57-63 Num( + "123u64", + ), + ), + [ + Newline, + ], + ), + @67-80 SpaceBefore( + RequiredValue( + @67-71 "u128", + [], + @73-80 Num( + "123u128", + ), + ), + [ + Newline, + ], + ), + @84-95 SpaceBefore( + RequiredValue( + @84-86 "i8", + [], + @90-95 Num( + "123i8", + ), + ), + [ + Newline, + ], + ), + @99-111 SpaceBefore( + RequiredValue( + @99-102 "i16", + [], + @105-111 Num( + "123i16", + ), + ), + [ + Newline, + ], + ), + @115-127 SpaceBefore( + RequiredValue( + @115-118 "i32", + [], + @121-127 Num( + "123i32", + ), + ), + [ + Newline, + ], + ), + @131-143 SpaceBefore( + RequiredValue( + @131-134 "i64", + [], + @137-143 Num( + "123i64", + ), + ), + [ + Newline, + ], + ), + @147-160 SpaceBefore( + RequiredValue( + @147-151 "i128", + [], + @153-160 Num( + "123i128", + ), + ), + [ + Newline, + ], + ), + @164-176 SpaceBefore( + RequiredValue( + @164-167 "dec", + [], + @170-176 Num( + "123dec", + ), + ), + [ + Newline, + ], + ), + @180-195 SpaceBefore( + RequiredValue( + @180-185 "u8Neg", + [], + @189-195 Num( + "-123u8", + ), + ), + [ + Newline, + ], + ), + @199-215 SpaceBefore( + RequiredValue( + @199-205 "u16Neg", + [], + @208-215 Num( + "-123u16", + ), + ), + [ + Newline, + ], + ), + @219-235 SpaceBefore( + RequiredValue( + @219-225 "u32Neg", + [], + @228-235 Num( + "-123u32", + ), + ), + [ + Newline, + ], + ), + @239-255 SpaceBefore( + RequiredValue( + @239-245 "u64Neg", + [], + @248-255 Num( + "-123u64", + ), + ), + [ + Newline, + ], + ), + @259-276 SpaceBefore( + RequiredValue( + @259-266 "u128Neg", + [], + @268-276 Num( + "-123u128", + ), + ), + [ + Newline, + ], + ), + @280-295 SpaceBefore( + RequiredValue( + @280-285 "i8Neg", + [], + @289-295 Num( + "-123i8", + ), + ), + [ + Newline, + ], + ), + @299-315 SpaceBefore( + RequiredValue( + @299-305 "i16Neg", + [], + @308-315 Num( + "-123i16", + ), + ), + [ + Newline, + ], + ), + @319-335 SpaceBefore( + RequiredValue( + @319-325 "i32Neg", + [], + @328-335 Num( + "-123i32", + ), + ), + [ + Newline, + ], + ), + @339-355 SpaceBefore( + RequiredValue( + @339-345 "i64Neg", + [], + @348-355 Num( + "-123i64", + ), + ), + [ + Newline, + ], + ), + @359-376 SpaceBefore( + RequiredValue( + @359-366 "i128Neg", + [], + @368-376 Num( + "-123i128", + ), + ), + [ + Newline, + ], + ), + @380-396 SpaceBefore( + RequiredValue( + @380-386 "decNeg", + [], + @389-396 Num( + "-123dec", + ), + ), + [ + Newline, + ], + ), + @400-416 SpaceBefore( + RequiredValue( + @400-405 "u8Bin", + [], + @409-416 NonBase10Int { + string: "101u8", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @420-437 SpaceBefore( + RequiredValue( + @420-426 "u16Bin", + [], + @429-437 NonBase10Int { + string: "101u16", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @441-458 SpaceBefore( + RequiredValue( + @441-447 "u32Bin", + [], + @450-458 NonBase10Int { + string: "101u32", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @462-479 SpaceBefore( + RequiredValue( + @462-468 "u64Bin", + [], + @471-479 NonBase10Int { + string: "101u64", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @483-501 SpaceBefore( + RequiredValue( + @483-490 "u128Bin", + [], + @492-501 NonBase10Int { + string: "101u128", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @505-521 SpaceBefore( + RequiredValue( + @505-510 "i8Bin", + [], + @514-521 NonBase10Int { + string: "101i8", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @525-542 SpaceBefore( + RequiredValue( + @525-531 "i16Bin", + [], + @534-542 NonBase10Int { + string: "101i16", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @546-563 SpaceBefore( + RequiredValue( + @546-552 "i32Bin", + [], + @555-563 NonBase10Int { + string: "101i32", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @567-584 SpaceBefore( + RequiredValue( + @567-573 "i64Bin", + [], + @576-584 NonBase10Int { + string: "101i64", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + @588-606 SpaceBefore( + RequiredValue( + @588-595 "i128Bin", + [], + @597-606 NonBase10Int { + string: "101i128", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc index 5be9c64e005..aeeef68b299 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc @@ -6,3 +6,4 @@ import cli.Stdout main = Stdout.line "hello" + diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast index bf6d78c970b..acb6368daa7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast @@ -56,7 +56,7 @@ Full { ], space_after: [ Slice(start = 3, length = 3), - Slice(start = 8, length = 0), + Slice(start = 8, length = 2), ], spaces: [ Newline, @@ -67,6 +67,8 @@ Full { Newline, Newline, Newline, + Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast index 963976ac908..dbbe6f46c44 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast @@ -1,33 +1,38 @@ SpaceBefore( - Backpassing( - [ - @18-19 Identifier { - ident: "x", - }, - ], - @23-32 ParensAround( - Closure( - [ - @25-26 Identifier { + SpaceAfter( + Backpassing( + [ + @18-19 Identifier { + ident: "x", + }, + ], + @23-32 ParensAround( + Closure( + [ + @25-26 Identifier { + ident: "y", + }, + ], + @30-31 Var { + module_name: "", ident: "y", }, - ], - @30-31 Var { + ), + ), + @34-35 SpaceBefore( + Var { module_name: "", - ident: "y", + ident: "x", }, + [ + Newline, + Newline, + ], ), ), - @34-35 SpaceBefore( - Var { - module_name: "", - ident: "x", - }, - [ - Newline, - Newline, - ], - ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc new file mode 100644 index 00000000000..3403a0c7f4b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc @@ -0,0 +1 @@ +"x" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast index e15ee75ac84..94dee6c8672 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast @@ -1,5 +1,10 @@ -Str( - PlainLine( - "x", +SpaceAfter( + Str( + PlainLine( + "x", + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast index d5d5b851447..4267eea0785 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @18-21, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @18-19 Identifier { - ident: "x", - }, - @20-21 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @18-21, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @18-19 Identifier { + ident: "x", + }, + @20-21 Num( + "5", + ), ), + ], + }, + @23-25 SpaceBefore( + Num( + "42", ), - ], - }, - @23-25 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc new file mode 100644 index 00000000000..577e02f2733 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc @@ -0,0 +1,4 @@ +# leading comment +x = 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast index 38c887ba9c0..8be31b142ba 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @18-23, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @18-19 Identifier { - ident: "x", - }, - @22-23 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @18-23, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @18-19 Identifier { + ident: "x", + }, + @22-23 Num( + "5", + ), ), + ], + }, + @25-27 SpaceBefore( + Num( + "42", ), - ], - }, - @25-27 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast index 152d1e596d6..759019618b4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast @@ -1,71 +1,76 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-22, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Apply( - @0-6 OpaqueRef( - "@Thunk", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-22, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-9 Apply( + @0-6 OpaqueRef( + "@Thunk", + ), + [ + @7-9 Identifier { + ident: "it", + }, + ], ), - [ - @7-9 Identifier { - ident: "it", + @12-22 Apply( + @12-14 Var { + module_name: "", + ident: "id", }, - ], - ), - @12-22 Apply( - @12-14 Var { - module_name: "", - ident: "id", - }, - [ - @16-21 ParensAround( - Apply( - @16-18 OpaqueRef( - "@A", - ), - [ - @19-21 Record( - [], + [ + @16-21 ParensAround( + Apply( + @16-18 OpaqueRef( + "@A", ), - ], - Space, + [ + @19-21 Record( + [], + ), + ], + Space, + ), ), - ), - ], - Space, + ], + Space, + ), ), + ], + }, + @23-28 SpaceBefore( + Apply( + @23-25 Var { + module_name: "", + ident: "it", + }, + [ + @26-28 Record( + [], + ), + ], + Space, ), - ], - }, - @23-28 SpaceBefore( - Apply( - @23-25 Var { - module_name: "", - ident: "it", - }, [ - @26-28 Record( - [], - ), + Newline, ], - Space, ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast index 6426fd40e66..c07edf253fb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast @@ -1,188 +1,98 @@ -Defs( - Defs { - tags: [ - Index(0), - Index(1), - Index(2), - Index(3), - Index(4), - Index(5), - Index(6), - Index(7), - Index(8), - Index(9), - ], - regions: [ - @0-7, - @31-62, - @86-117, - @146-153, - @189-196, - @224-231, - @265-272, - @306-313, - @329-360, - @401-408, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 2), - Slice(start = 2, length = 2), - Slice(start = 4, length = 2), - Slice(start = 6, length = 2), - Slice(start = 8, length = 2), - Slice(start = 10, length = 2), - Slice(start = 12, length = 2), - Slice(start = 14, length = 2), - Slice(start = 16, length = 2), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - Slice(start = 4, length = 0), - Slice(start = 6, length = 0), - Slice(start = 8, length = 0), - Slice(start = 10, length = 0), - Slice(start = 12, length = 0), - Slice(start = 14, length = 0), - Slice(start = 16, length = 0), - Slice(start = 18, length = 0), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [ - Opaque { - header: TypeHeader { - name: @0-1 "A", - vars: [], - }, - typ: @5-7 Apply( - "", - "U8", - [], - ), - derived: Some( - @19-29 Implements( - [ - @20-22 ImplementsAbility { - ability: @20-22 Apply( - "", - "Eq", - [], - ), - impls: None, - }, - @24-28 ImplementsAbility { - ability: @24-28 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @31-32 "A", - vars: [], - }, - typ: @36-62 Where( - @36-37 BoundVariable( - "a", - ), - [ - @44-62 ImplementsClause { - var: @44-45 "a", - abilities: [ - @57-62 Apply( - "", - "Other", - [], - ), - ], - }, - ], - ), - derived: Some( - @74-84 Implements( - [ - @75-77 ImplementsAbility { - ability: @75-77 Apply( - "", - "Eq", - [], - ), - impls: None, - }, - @79-83 ImplementsAbility { - ability: @79-83 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @86-87 "A", - vars: [], - }, - typ: @91-117 Where( - @91-92 BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + Index(1), + Index(2), + Index(3), + Index(4), + Index(5), + Index(6), + Index(7), + Index(8), + Index(9), + ], + regions: [ + @0-29, + @31-84, + @86-144, + @146-187, + @189-222, + @224-263, + @265-304, + @306-327, + @329-399, + @401-427, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + Slice(start = 4, length = 2), + Slice(start = 6, length = 2), + Slice(start = 8, length = 2), + Slice(start = 10, length = 2), + Slice(start = 12, length = 2), + Slice(start = 14, length = 2), + Slice(start = 16, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + Slice(start = 6, length = 0), + Slice(start = 8, length = 0), + Slice(start = 10, length = 0), + Slice(start = 12, length = 0), + Slice(start = 14, length = 0), + Slice(start = 16, length = 0), + Slice(start = 18, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [ + Opaque { + header: TypeHeader { + name: @0-1 "A", + vars: [], + }, + typ: @5-7 Apply( + "", + "U8", + [], ), - [ - @99-117 ImplementsClause { - var: @99-100 "a", - abilities: [ - @112-117 Apply( - "", - "Other", - [], - ), - ], - }, - ], - ), - derived: Some( - @134-144 SpaceBefore( - Implements( + derived: Some( + @19-29 Implements( [ - @135-137 ImplementsAbility { - ability: @135-137 Apply( + @20-22 ImplementsAbility { + ability: @20-22 Apply( "", "Eq", [], ), impls: None, }, - @139-143 ImplementsAbility { - ability: @139-143 Apply( + @24-28 ImplementsAbility { + ability: @24-28 Apply( "", "Hash", [], @@ -191,254 +101,267 @@ Defs( }, ], ), - [ - Newline, - ], ), - ), - }, - Opaque { - header: TypeHeader { - name: @146-147 "A", - vars: [], }, - typ: @151-153 Apply( - "", - "U8", - [], - ), - derived: Some( - @165-187 Implements( + Opaque { + header: TypeHeader { + name: @31-32 "A", + vars: [], + }, + typ: @36-62 Where( + @36-37 BoundVariable( + "a", + ), [ - @166-173 ImplementsAbility { - ability: @166-168 Apply( - "", - "Eq", - [], - ), - impls: Some( - @169-173 AbilityImpls( - [ - @170-172 LabelOnly( - @170-172 "eq", - ), - ], - ), - ), - }, - @175-186 ImplementsAbility { - ability: @175-179 Apply( - "", - "Hash", - [], - ), - impls: Some( - @180-186 AbilityImpls( - [ - @181-185 LabelOnly( - @181-185 "hash", - ), - ], + @44-62 ImplementsClause { + var: @44-45 "a", + abilities: [ + @57-62 Apply( + "", + "Other", + [], ), - ), + ], }, ], ), - ), - }, - Opaque { - header: TypeHeader { - name: @189-190 "A", - vars: [], - }, - typ: @194-196 Apply( - "", - "U8", - [], - ), - derived: Some( - @208-222 Implements( - [ - @209-221 ImplementsAbility { - ability: @209-211 Apply( - "", - "Eq", - [], - ), - impls: Some( - @212-221 AbilityImpls( - [ - @213-215 LabelOnly( - @213-215 "eq", - ), - @217-220 LabelOnly( - @217-220 "eq1", - ), - ], + derived: Some( + @74-84 Implements( + [ + @75-77 ImplementsAbility { + ability: @75-77 Apply( + "", + "Eq", + [], ), - ), - }, - ], + impls: None, + }, + @79-83 ImplementsAbility { + ability: @79-83 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), ), - ), - }, - Opaque { - header: TypeHeader { - name: @224-225 "A", - vars: [], }, - typ: @229-231 Apply( - "", - "U8", - [], - ), - derived: Some( - @243-263 Implements( + Opaque { + header: TypeHeader { + name: @86-87 "A", + vars: [], + }, + typ: @91-117 Where( + @91-92 BoundVariable( + "a", + ), [ - @244-256 ImplementsAbility { - ability: @244-246 Apply( - "", - "Eq", - [], - ), - impls: Some( - @247-256 AbilityImpls( - [ - @248-250 LabelOnly( - @248-250 "eq", - ), - @252-255 LabelOnly( - @252-255 "eq1", - ), - ], + @99-117 ImplementsClause { + var: @99-100 "a", + abilities: [ + @112-117 Apply( + "", + "Other", + [], ), - ), - }, - @258-262 ImplementsAbility { - ability: @258-262 Apply( - "", - "Hash", - [], - ), - impls: None, + ], }, ], ), - ), - }, - Opaque { - header: TypeHeader { - name: @265-266 "A", - vars: [], + derived: Some( + @134-144 SpaceBefore( + Implements( + [ + @135-137 ImplementsAbility { + ability: @135-137 Apply( + "", + "Eq", + [], + ), + impls: None, + }, + @139-143 ImplementsAbility { + ability: @139-143 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), + [ + Newline, + ], + ), + ), }, - typ: @270-272 Apply( - "", - "U8", - [], - ), - derived: Some( - @284-304 Implements( - [ - @285-289 ImplementsAbility { - ability: @285-289 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - @291-303 ImplementsAbility { - ability: @291-293 Apply( - "", - "Eq", - [], - ), - impls: Some( - @294-303 AbilityImpls( - [ - @295-297 LabelOnly( - @295-297 "eq", - ), - @299-302 LabelOnly( - @299-302 "eq1", - ), - ], + Opaque { + header: TypeHeader { + name: @146-147 "A", + vars: [], + }, + typ: @151-153 Apply( + "", + "U8", + [], + ), + derived: Some( + @165-187 Implements( + [ + @166-173 ImplementsAbility { + ability: @166-168 Apply( + "", + "Eq", + [], ), - ), - }, - ], + impls: Some( + @169-173 AbilityImpls( + [ + @170-172 LabelOnly( + @170-172 "eq", + ), + ], + ), + ), + }, + @175-186 ImplementsAbility { + ability: @175-179 Apply( + "", + "Hash", + [], + ), + impls: Some( + @180-186 AbilityImpls( + [ + @181-185 LabelOnly( + @181-185 "hash", + ), + ], + ), + ), + }, + ], + ), ), - ), - }, - Opaque { - header: TypeHeader { - name: @306-307 "A", - vars: [], }, - typ: @311-313 Apply( - "", - "U8", - [], - ), - derived: Some( - @325-327 Implements( + Opaque { + header: TypeHeader { + name: @189-190 "A", + vars: [], + }, + typ: @194-196 Apply( + "", + "U8", [], ), - ), - }, - Opaque { - header: TypeHeader { - name: @329-330 "A", - vars: [], + derived: Some( + @208-222 Implements( + [ + @209-221 ImplementsAbility { + ability: @209-211 Apply( + "", + "Eq", + [], + ), + impls: Some( + @212-221 AbilityImpls( + [ + @213-215 LabelOnly( + @213-215 "eq", + ), + @217-220 LabelOnly( + @217-220 "eq1", + ), + ], + ), + ), + }, + ], + ), + ), }, - typ: @334-360 Where( - @334-335 BoundVariable( - "a", + Opaque { + header: TypeHeader { + name: @224-225 "A", + vars: [], + }, + typ: @229-231 Apply( + "", + "U8", + [], ), - [ - @342-360 ImplementsClause { - var: @342-343 "a", - abilities: [ - @355-360 Apply( - "", - "Other", - [], - ), - ], - }, - ], - ), - derived: Some( - @377-399 SpaceBefore( - Implements( + derived: Some( + @243-263 Implements( [ - @378-385 ImplementsAbility { - ability: @378-380 Apply( + @244-256 ImplementsAbility { + ability: @244-246 Apply( "", "Eq", [], ), impls: Some( - @381-385 AbilityImpls( + @247-256 AbilityImpls( [ - @382-384 LabelOnly( - @382-384 "eq", + @248-250 LabelOnly( + @248-250 "eq", + ), + @252-255 LabelOnly( + @252-255 "eq1", ), ], ), ), }, - @387-398 ImplementsAbility { - ability: @387-391 Apply( + @258-262 ImplementsAbility { + ability: @258-262 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @265-266 "A", + vars: [], + }, + typ: @270-272 Apply( + "", + "U8", + [], + ), + derived: Some( + @284-304 Implements( + [ + @285-289 ImplementsAbility { + ability: @285-289 Apply( "", "Hash", [], ), + impls: None, + }, + @291-303 ImplementsAbility { + ability: @291-293 Apply( + "", + "Eq", + [], + ), impls: Some( - @392-398 AbilityImpls( + @294-303 AbilityImpls( [ - @393-397 LabelOnly( - @393-397 "hash", + @295-297 LabelOnly( + @295-297 "eq", + ), + @299-302 LabelOnly( + @299-302 "eq1", ), ], ), @@ -446,51 +369,133 @@ Defs( }, ], ), - [ - Newline, - ], ), - ), - }, - Opaque { - header: TypeHeader { - name: @401-402 "A", - vars: [], }, - typ: @406-408 Apply( - "", - "U8", - [], - ), - derived: Some( - @420-427 Implements( + Opaque { + header: TypeHeader { + name: @306-307 "A", + vars: [], + }, + typ: @311-313 Apply( + "", + "U8", + [], + ), + derived: Some( + @325-327 Implements( + [], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @329-330 "A", + vars: [], + }, + typ: @334-360 Where( + @334-335 BoundVariable( + "a", + ), [ - @421-426 ImplementsAbility { - ability: @421-423 Apply( - "", - "Eq", - [], - ), - impls: Some( - @424-426 AbilityImpls( + @342-360 ImplementsClause { + var: @342-343 "a", + abilities: [ + @355-360 Apply( + "", + "Other", [], ), - ), + ], }, ], ), - ), - }, - ], - value_defs: [], - }, - @429-430 SpaceBefore( - Num( - "0", + derived: Some( + @377-399 SpaceBefore( + Implements( + [ + @378-385 ImplementsAbility { + ability: @378-380 Apply( + "", + "Eq", + [], + ), + impls: Some( + @381-385 AbilityImpls( + [ + @382-384 LabelOnly( + @382-384 "eq", + ), + ], + ), + ), + }, + @387-398 ImplementsAbility { + ability: @387-391 Apply( + "", + "Hash", + [], + ), + impls: Some( + @392-398 AbilityImpls( + [ + @393-397 LabelOnly( + @393-397 "hash", + ), + ], + ), + ), + }, + ], + ), + [ + Newline, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @401-402 "A", + vars: [], + }, + typ: @406-408 Apply( + "", + "U8", + [], + ), + derived: Some( + @420-427 Implements( + [ + @421-426 ImplementsAbility { + ability: @421-423 Apply( + "", + "Eq", + [], + ), + impls: Some( + @424-426 AbilityImpls( + [], + ), + ), + }, + ], + ), + ), + }, + ], + value_defs: [], + }, + @429-430 SpaceBefore( + Num( + "0", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc new file mode 100644 index 00000000000..320a3f57749 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc @@ -0,0 +1 @@ +@Age \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast index 4dfbe2dc653..620e120d1b5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast @@ -1,3 +1,8 @@ -OpaqueRef( - "@Age", +SpaceAfter( + OpaqueRef( + "@Age", + ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc new file mode 100644 index 00000000000..0aeddca9ea6 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc @@ -0,0 +1 @@ +@Age m n \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast index c42661df165..3ad5fad17d6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast @@ -1,16 +1,21 @@ -Apply( - @0-4 OpaqueRef( - "@Age", +SpaceAfter( + Apply( + @0-4 OpaqueRef( + "@Age", + ), + [ + @5-6 Var { + module_name: "", + ident: "m", + }, + @7-8 Var { + module_name: "", + ident: "n", + }, + ], + Space, ), [ - @5-6 Var { - module_name: "", - ident: "m", - }, - @7-8 Var { - module_name: "", - ident: "n", - }, + Newline, ], - Space, ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast index 0dd1a3462ed..146d18a99e0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast @@ -1,24 +1,29 @@ -When( - @5-6 Var { - module_name: "", - ident: "n", - }, - [ - WhenBranch { - patterns: [ - @12-16 SpaceBefore( - OpaqueRef( - "@Age", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "n", + }, + [ + WhenBranch { + patterns: [ + @12-16 SpaceBefore( + OpaqueRef( + "@Age", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @20-21 Num( + "1", ), - ], - value: @20-21 Num( - "1", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast index 9694879ba4f..eb07397e0ce 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast @@ -1,46 +1,51 @@ -When( - @5-6 Var { - module_name: "", - ident: "n", - }, - [ - WhenBranch { - patterns: [ - @12-20 SpaceBefore( - Apply( - @12-16 OpaqueRef( - "@Add", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "n", + }, + [ + WhenBranch { + patterns: [ + @12-20 SpaceBefore( + Apply( + @12-16 OpaqueRef( + "@Add", + ), + [ + @17-18 Identifier { + ident: "n", + }, + @19-20 Identifier { + ident: "m", + }, + ], ), [ - @17-18 Identifier { - ident: "n", - }, - @19-20 Identifier { - ident: "m", - }, + Newline, ], ), + ], + value: @24-29 BinOps( [ - Newline, + ( + @24-25 Var { + module_name: "", + ident: "n", + }, + @26-27 Plus, + ), ], + @28-29 Var { + module_name: "", + ident: "m", + }, ), - ], - value: @24-29 BinOps( - [ - ( - @24-25 Var { - module_name: "", - ident: "n", - }, - @26-27 Plus, - ), - ], - @28-29 Var { - module_name: "", - ident: "m", - }, - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc deleted file mode 100644 index 151cb0215e7..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -Age := U8 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast index 4184232f3cd..d1464898dbb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [ Opaque { header: TypeHeader { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc deleted file mode 100644 index d8c2642763f..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -a : e -Na := - e -e0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast deleted file mode 100644 index 0ee155bf939..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast +++ /dev/null @@ -1,54 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(0), - ], - regions: [ - @0-3, - @4-11, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - spaces: [ - Newline, - ], - type_defs: [ - Opaque { - header: TypeHeader { - name: @4-6 "Na", - vars: [], - }, - typ: @10-11 SpaceBefore( - BoundVariable( - "e", - ), - [ - Newline, - ], - ), - derived: None, - }, - ], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "a", - }, - @2-3 BoundVariable( - "e", - ), - ), - ], - }, - @12-14 Var { - module_name: "", - ident: "e0", - }, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast index f60cc590b6e..552e8eb2bde 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast @@ -9,13 +9,15 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [ Opaque { header: TypeHeader { - name: @0-10 "Bookmark", + name: @0-8 "Bookmark", vars: [ @9-10 Identifier { ident: "a", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast index bad6ae56512..5599969492a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast @@ -1,76 +1,81 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-29, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @4-29 Apply( - @4-7 Var { - module_name: "", - ident: "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-29, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", }, - [ - @9-28 ParensAround( - Apply( - @9-12 Var { - module_name: "", - ident: "baz", - }, - [ - @13-28 Record( - [ - @17-26 SpaceBefore( - SpaceAfter( - RequiredValue( - @17-20 "bar", - [], - @22-26 Var { - module_name: "", - ident: "blah", - }, + @4-29 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @9-28 ParensAround( + Apply( + @9-12 Var { + module_name: "", + ident: "baz", + }, + [ + @13-28 Record( + [ + @17-26 SpaceBefore( + SpaceAfter( + RequiredValue( + @17-20 "bar", + [], + @22-26 Var { + module_name: "", + ident: "blah", + }, + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], - ), - ], - Space, + ], + ), + ], + Space, + ), ), - ), - ], - Space, + ], + Space, + ), ), - ), - ], - }, - @30-31 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], }, - [ - Newline, - ], + @30-31 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast index c99bc7d791f..e3a47bfc4e4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast @@ -1,72 +1,77 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-22, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @4-22 Apply( - @4-7 Var { - module_name: "", - ident: "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-22, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", }, - [ - @8-22 Record( - [ - @10-20 SpaceBefore( - SpaceAfter( - RequiredValue( - @10-13 "bar", - [ - Newline, - ], - @16-20 SpaceBefore( - Var { - module_name: "", - ident: "blah", - }, + @4-22 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @8-22 Record( + [ + @10-20 SpaceBefore( + SpaceAfter( + RequiredValue( + @10-13 "bar", [ Newline, ], + @16-20 SpaceBefore( + Var { + module_name: "", + ident: "blah", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], - ), - ], - Space, + ], + ), + ], + Space, + ), ), - ), - ], - }, - @23-24 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], }, - [ - Newline, - ], + @23-24 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast index 7796fc43a06..2e05eed0179 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast @@ -1,57 +1,62 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-17, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "a", - }, - @4-17 List( - [ - @8-9 SpaceBefore( - Num( - "1", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-17, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "a", + }, + @4-17 List( + [ + @8-9 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @11-12 Num( - "2", - ), - @14-15 SpaceAfter( - Num( - "3", + @11-12 Num( + "2", ), - [ - Newline, - ], - ), - ], + @14-15 SpaceAfter( + Num( + "3", + ), + [ + Newline, + ], + ), + ], + ), ), - ), - ], - }, - @18-19 SpaceBefore( - Var { - module_name: "", - ident: "a", + ], }, - [ - Newline, - ], + @18-19 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast index 3b9dba71142..60ae689a943 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast @@ -1,65 +1,70 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-23, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @4-23 Apply( - @4-7 Var { - module_name: "", - ident: "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-23, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", }, - [ - @8-23 Record( - [ - @12-21 SpaceBefore( - SpaceAfter( - RequiredValue( - @12-15 "bar", - [], - @17-21 Var { - module_name: "", - ident: "blah", - }, + @4-23 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @8-23 Record( + [ + @12-21 SpaceBefore( + SpaceAfter( + RequiredValue( + @12-15 "bar", + [], + @17-21 Var { + module_name: "", + ident: "blah", + }, + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], - ), - ], - Space, + ], + ), + ], + Space, + ), ), - ), - ], - }, - @24-25 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], }, - [ - Newline, - ], + @24-25 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc deleted file mode 100644 index 5b6326454b1..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc +++ /dev/null @@ -1,3 +0,0 @@ -i # -N : b -a \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast deleted file mode 100644 index 57adf1d09e2..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast +++ /dev/null @@ -1,52 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-9, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Apply( - @0-1 Identifier { - ident: "i", - }, - [ - @5-6 SpaceBefore( - Tag( - "N", - ), - [ - Newline, - LineComment( - "", - ), - ], - ), - ], - ), - @8-9 BoundVariable( - "b", - ), - ), - ], - }, - @10-11 SpaceBefore( - Var { - module_name: "", - ident: "a", - }, - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc deleted file mode 100644 index 280f36ca111..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc +++ /dev/null @@ -1,4 +0,0 @@ -i -(# -N):b -a \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast index fbc4c78d44c..0e588aed8e9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast @@ -4,7 +4,7 @@ Defs( Index(0), ], regions: [ - @1-5, + @0-5, ], space_before: [ Slice(start = 0, length = 0), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast index 3a8d3030da6..0e7991f580e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast @@ -4,7 +4,7 @@ Defs( Index(0), ], regions: [ - @2-6, + @0-6, ], space_before: [ Slice(start = 0, length = 0), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc new file mode 100644 index 00000000000..d5b68ad0519 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc @@ -0,0 +1,3 @@ +Blah a b : Foo.Bar.Baz x y + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast index 39188e402bc..bddfadf4bbc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast @@ -1,54 +1,59 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Alias { - header: TypeHeader { - name: @0-4 "Blah", - vars: [ - @5-6 Identifier { - ident: "a", - }, - @7-8 Identifier { - ident: "b", - }, - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-26, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Alias { + header: TypeHeader { + name: @0-4 "Blah", + vars: [ + @5-6 Identifier { + ident: "a", + }, + @7-8 Identifier { + ident: "b", + }, + ], + }, + ann: @11-26 Apply( + "Foo.Bar", + "Baz", + [ + @23-24 BoundVariable( + "x", + ), + @25-26 BoundVariable( + "y", + ), + ], + ), }, - ann: @11-26 Apply( - "Foo.Bar", - "Baz", - [ - @23-24 BoundVariable( - "x", - ), - @25-26 BoundVariable( - "y", - ), - ], - ), - }, - ], - value_defs: [], - }, - @28-30 SpaceBefore( - Num( - "42", + ], + value_defs: [], + }, + @28-30 SpaceBefore( + Num( + "42", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc new file mode 100644 index 00000000000..c24a0d5326d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc @@ -0,0 +1,3 @@ +foo : Foo.Bar.Baz x y as Blah a b + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast index c8c4f05ac39..b2090559666 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast @@ -1,60 +1,65 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-33, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-3 Identifier { - ident: "foo", - }, - @6-33 As( - @6-21 Apply( - "Foo.Bar", - "Baz", - [ - @18-19 BoundVariable( - "x", - ), - @20-21 BoundVariable( - "y", - ), - ], - ), - [], - TypeHeader { - name: @25-29 "Blah", - vars: [ - @30-31 Identifier { - ident: "a", - }, - @32-33 Identifier { - ident: "b", - }, - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-33, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-3 Identifier { + ident: "foo", }, + @6-33 As( + @6-21 Apply( + "Foo.Bar", + "Baz", + [ + @18-19 BoundVariable( + "x", + ), + @20-21 BoundVariable( + "y", + ), + ], + ), + [], + TypeHeader { + name: @25-29 "Blah", + vars: [ + @30-31 Identifier { + ident: "a", + }, + @32-33 Identifier { + ident: "b", + }, + ], + }, + ), ), + ], + }, + @35-37 SpaceBefore( + Num( + "42", ), - ], - }, - @35-37 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc new file mode 100644 index 00000000000..0d1be03beaf --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc @@ -0,0 +1,2 @@ +when 0 is + _ as n -> n \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast index 2a3831a554a..68f01fd5521 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast @@ -1,30 +1,35 @@ -When( - @5-6 Num( - "0", - ), - [ - WhenBranch { - patterns: [ - @14-20 SpaceBefore( - As( - @14-15 Underscore( - "", +SpaceAfter( + When( + @5-6 Num( + "0", + ), + [ + WhenBranch { + patterns: [ + @14-20 SpaceBefore( + As( + @14-15 Underscore( + "", + ), + PatternAs { + spaces_before: [], + identifier: @19-20 "n", + }, ), - PatternAs { - spaces_before: [], - identifier: @19-20 "n", - }, + [ + Newline, + ], ), - [ - Newline, - ], - ), - ], - value: @24-25 Var { - module_name: "", - ident: "n", + ], + value: @24-25 Var { + module_name: "", + ident: "n", + }, + guard: None, }, - guard: None, - }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc new file mode 100644 index 00000000000..87d7a340478 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc @@ -0,0 +1,2 @@ +when myList is + [first, .. as rest] -> 0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast index ff0aec60dc6..c068d9ce2bc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast @@ -1,39 +1,44 @@ -When( - @5-11 Var { - module_name: "", - ident: "myList", - }, - [ - WhenBranch { - patterns: [ - @19-38 SpaceBefore( - List( - [ - @20-25 Identifier { - ident: "first", - }, - @27-37 ListRest( - Some( - ( - [], - PatternAs { - spaces_before: [], - identifier: @33-37 "rest", - }, +SpaceAfter( + When( + @5-11 Var { + module_name: "", + ident: "myList", + }, + [ + WhenBranch { + patterns: [ + @19-38 SpaceBefore( + List( + [ + @20-25 Identifier { + ident: "first", + }, + @27-37 ListRest( + Some( + ( + [], + PatternAs { + spaces_before: [], + identifier: @33-37 "rest", + }, + ), ), ), - ), + ], + ), + [ + Newline, ], ), - [ - Newline, - ], + ], + value: @42-43 Num( + "0", ), - ], - value: @42-43 Num( - "0", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc new file mode 100644 index 00000000000..58f487d11e6 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc @@ -0,0 +1,4 @@ +when 0 is + 0 # foobar + as # barfoo + n -> {} \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast index 83e37e7658e..caf964ee71e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Num( - "0", - ), - [ - WhenBranch { - patterns: [ - @14-54 SpaceBefore( - As( - @14-15 SpaceAfter( - NumLiteral( - "0", - ), - [ - LineComment( - " foobar", +SpaceAfter( + When( + @5-6 Num( + "0", + ), + [ + WhenBranch { + patterns: [ + @14-54 SpaceBefore( + As( + @14-15 SpaceAfter( + NumLiteral( + "0", ), - ], + [ + LineComment( + " foobar", + ), + ], + ), + PatternAs { + spaces_before: [ + LineComment( + " barfoo", + ), + ], + identifier: @53-54 "n", + }, ), - PatternAs { - spaces_before: [ - LineComment( - " barfoo", - ), - ], - identifier: @53-54 "n", - }, + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @58-60 Record( + [], ), - ], - value: @58-60 Record( - [], - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast index a6c26c85004..64d1d66e823 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast @@ -1,81 +1,86 @@ -When( - @5-22 Apply( - @5-11 Tag( - "Delmin", +SpaceAfter( + When( + @5-22 Apply( + @5-11 Tag( + "Delmin", + ), + [ + @13-19 ParensAround( + Apply( + @13-16 Tag( + "Del", + ), + [ + @17-19 Var { + module_name: "", + ident: "rx", + }, + ], + Space, + ), + ), + @21-22 Num( + "0", + ), + ], + Space, ), [ - @13-19 ParensAround( - Apply( - @13-16 Tag( - "Del", + WhenBranch { + patterns: [ + @30-48 SpaceBefore( + Apply( + @30-36 Tag( + "Delmin", + ), + [ + @38-44 Apply( + @38-41 Tag( + "Del", + ), + [ + @42-44 Identifier { + ident: "ry", + }, + ], + ), + @47-48 Underscore( + "", + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @52-78 Apply( + @52-56 Tag( + "Node", ), [ - @17-19 Var { + @57-62 Tag( + "Black", + ), + @63-64 Num( + "0", + ), + @65-75 Var { + module_name: "Bool", + ident: "false", + }, + @76-78 Var { module_name: "", - ident: "rx", + ident: "ry", }, ], Space, ), - ), - @21-22 Num( - "0", - ), + guard: None, + }, ], - Space, ), [ - WhenBranch { - patterns: [ - @30-48 SpaceBefore( - Apply( - @30-36 Tag( - "Delmin", - ), - [ - @38-44 Apply( - @38-41 Tag( - "Del", - ), - [ - @42-44 Identifier { - ident: "ry", - }, - ], - ), - @47-48 Underscore( - "", - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @52-78 Apply( - @52-56 Tag( - "Node", - ), - [ - @57-62 Tag( - "Black", - ), - @63-64 Num( - "0", - ), - @65-75 Var { - module_name: "Bool", - ident: "false", - }, - @76-78 Var { - module_name: "", - ident: "ry", - }, - ], - Space, - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc new file mode 100644 index 00000000000..047cd4eb6ac --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc @@ -0,0 +1 @@ +1 * if Bool.true then 1 else 1 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast index 3375d052187..91571381880 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast @@ -1,26 +1,31 @@ -BinOps( - [ - ( - @0-1 Num( - "1", - ), - @2-3 Star, - ), - ], - @4-30 If( +SpaceAfter( + BinOps( [ ( - @7-16 Var { - module_name: "Bool", - ident: "true", - }, - @22-23 Num( + @0-1 Num( "1", ), + @2-3 Star, ), ], - @29-30 Num( - "1", + @4-30 If( + [ + ( + @7-16 Var { + module_name: "Bool", + ident: "true", + }, + @22-23 Num( + "1", + ), + ), + ], + @29-30 Num( + "1", + ), ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast index 15d220aa1c2..9b1bd339926 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast @@ -1,54 +1,59 @@ -BinOps( - [ - ( - @0-1 Num( - "1", - ), - @2-3 Plus, - ), - ], - @8-53 SpaceBefore( - When( - @13-16 Tag( - "Foo", +SpaceAfter( + BinOps( + [ + ( + @0-1 Num( + "1", + ), + @2-3 Plus, ), - [ - WhenBranch { - patterns: [ - @28-31 SpaceBefore( - Tag( - "Foo", + ], + @8-53 SpaceBefore( + When( + @13-16 Tag( + "Foo", + ), + [ + WhenBranch { + patterns: [ + @28-31 SpaceBefore( + Tag( + "Foo", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @35-36 Num( + "2", ), - ], - value: @35-36 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @45-48 SpaceBefore( - Tag( - "Bar", + guard: None, + }, + WhenBranch { + patterns: [ + @45-48 SpaceBefore( + Tag( + "Bar", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @52-53 Num( + "3", ), - ], - value: @52-53 Num( - "3", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc deleted file mode 100644 index 407c12d3341..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -app [quicksort, Flags, Model] { pf: "./platform" } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc new file mode 100644 index 00000000000..4c8ea1d6f4f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc @@ -0,0 +1,5 @@ +# leading comment +{ x, y } = 5 +y = 6 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast index 6f64d4ce7d7..961dca80ba7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast @@ -1,61 +1,66 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @18-30, - @31-36, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @18-26 RecordDestructure( - [ - @20-21 Identifier { - ident: "x", - }, - @23-25 Identifier { - ident: "y", - }, - ], + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @18-30, + @31-36, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @18-26 RecordDestructure( + [ + @20-21 Identifier { + ident: "x", + }, + @23-25 Identifier { + ident: "y", + }, + ], + ), + @29-30 Num( + "5", + ), ), - @29-30 Num( - "5", + Body( + @31-32 Identifier { + ident: "y", + }, + @35-36 Num( + "6", + ), ), + ], + }, + @38-40 SpaceBefore( + Num( + "42", ), - Body( - @31-32 Identifier { - ident: "y", - }, - @35-36 Num( - "6", - ), - ), - ], - }, - @38-40 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast index d2378aaaccd..5ecb61a2324 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast @@ -1,128 +1,133 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-122, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @8-122 SpaceBefore( - Record { - fields: [ - @18-38 SpaceBefore( - RequiredValue( - @18-25 "getLine", - [], - @28-38 Apply( - "", - "Effect", - [ - @35-38 Apply( - "", - "Str", - [], - ), - ], - ), - ), - [ - Newline, - ], - ), - @48-75 SpaceBefore( - RequiredValue( - @48-55 "putLine", - [], - @58-75 Function( - [ - @58-61 Apply( - "", - "Str", - [], - ), - ], - @65-75 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-122, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @8-122 SpaceBefore( + Record { + fields: [ + @18-38 SpaceBefore( + RequiredValue( + @18-25 "getLine", + [], + @28-38 Apply( "", "Effect", [ - @72-75 Apply( + @35-38 Apply( "", - "Int", + "Str", [], ), ], ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @85-94 SpaceBefore( - RequiredValue( - @85-89 "text", - [], - @91-94 Apply( - "", - "Str", + @48-75 SpaceBefore( + RequiredValue( + @48-55 "putLine", [], + @58-75 Function( + [ + @58-61 Apply( + "", + "Str", + [], + ), + ], + @65-75 Apply( + "", + "Effect", + [ + @72-75 Apply( + "", + "Int", + [], + ), + ], + ), + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @104-116 SpaceBefore( - SpaceAfter( + @85-94 SpaceBefore( RequiredValue( - @104-109 "value", + @85-89 "text", [], - @111-116 Apply( + @91-94 Apply( "", - "Int", - [ - @115-116 Wildcard, - ], + "Str", + [], + ), + ), + [ + Newline, + ], + ), + @104-116 SpaceBefore( + SpaceAfter( + RequiredValue( + @104-109 "value", + [], + @111-116 Apply( + "", + "Int", + [ + @115-116 Wildcard, + ], + ), ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), + ], + ext: None, + }, + [ + Newline, ], - ext: None, - }, - [ - Newline, - ], + ), ), + ], + }, + @124-126 SpaceBefore( + Num( + "42", ), - ], - }, - @124-126 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc new file mode 100644 index 00000000000..0f373a10463 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc @@ -0,0 +1,3 @@ +x : { init : {} -> Model, update : Model, Str -> Model, view : Model -> Str } + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast index 925374f414b..075390b3f72 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast @@ -1,97 +1,102 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-77, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "x", - }, - @4-77 Record { - fields: [ - @6-24 RequiredValue( - @6-10 "init", - [], - @13-24 Function( - [ - @13-15 Record { - fields: [], - ext: None, - }, - ], - @19-24 Apply( - "", - "Model", - [], - ), - ), - ), - @26-54 RequiredValue( - @26-32 "update", - [], - @35-54 Function( - [ - @35-40 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-77, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "x", + }, + @4-77 Record { + fields: [ + @6-24 RequiredValue( + @6-10 "init", + [], + @13-24 Function( + [ + @13-15 Record { + fields: [], + ext: None, + }, + ], + @19-24 Apply( "", "Model", [], ), - @42-45 Apply( + ), + ), + @26-54 RequiredValue( + @26-32 "update", + [], + @35-54 Function( + [ + @35-40 Apply( + "", + "Model", + [], + ), + @42-45 Apply( + "", + "Str", + [], + ), + ], + @49-54 Apply( "", - "Str", + "Model", [], ), - ], - @49-54 Apply( - "", - "Model", - [], ), ), - ), - @56-75 RequiredValue( - @56-60 "view", - [], - @63-75 Function( - [ - @63-68 Apply( + @56-75 RequiredValue( + @56-60 "view", + [], + @63-75 Function( + [ + @63-68 Apply( + "", + "Model", + [], + ), + ], + @72-75 Apply( "", - "Model", + "Str", [], ), - ], - @72-75 Apply( - "", - "Str", - [], ), ), - ), - ], - ext: None, - }, + ], + ext: None, + }, + ), + ], + }, + @79-81 SpaceBefore( + Num( + "42", ), - ], - }, - @79-81 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast new file mode 100644 index 00000000000..61c3ba5ea80 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast @@ -0,0 +1,137 @@ +Defs { + tags: [ + Index(0), + Index(1), + Index(2), + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @0-60, + @61-105, + @106-160, + @162-200, + @201-233, + @234-266, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + Slice(start = 2, length = 2), + Slice(start = 4, length = 1), + Slice(start = 5, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + Slice(start = 5, length = 0), + Slice(start = 6, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [ + Alias { + header: TypeHeader { + name: @0-3 "App", + vars: [ + @4-9 Identifier { + ident: "state", + }, + @10-18 Identifier { + ident: "initData", + }, + ], + }, + ann: @21-60 Apply( + "Html.Internal.Shared", + "App", + [ + @46-51 BoundVariable( + "state", + ), + @52-60 BoundVariable( + "initData", + ), + ], + ), + }, + Alias { + header: TypeHeader { + name: @61-65 "Html", + vars: [ + @66-71 Identifier { + ident: "state", + }, + ], + }, + ann: @74-105 Apply( + "Html.Internal.Shared", + "Html", + [ + @100-105 BoundVariable( + "state", + ), + ], + ), + }, + Alias { + header: TypeHeader { + name: @106-115 "Attribute", + vars: [ + @116-121 Identifier { + ident: "state", + }, + ], + }, + ann: @124-160 Apply( + "Html.Internal.Shared", + "Attribute", + [ + @155-160 BoundVariable( + "state", + ), + ], + ), + }, + ], + value_defs: [ + Body( + @162-169 Identifier { + ident: "element", + }, + @172-200 Var { + module_name: "Html.Internal.Shared", + ident: "element", + }, + ), + Body( + @201-205 Identifier { + ident: "text", + }, + @208-233 Var { + module_name: "Html.Internal.Shared", + ident: "text", + }, + ), + Body( + @234-238 Identifier { + ident: "none", + }, + @241-266 Var { + module_name: "Html.Internal.Shared", + ident: "none", + }, + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc new file mode 100644 index 00000000000..20d2977aac0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc @@ -0,0 +1,7 @@ +App state initData : Html.Internal.Shared.App state initData +Html state : Html.Internal.Shared.Html state +Attribute state : Html.Internal.Shared.Attribute state + +element = Html.Internal.Shared.element +text = Html.Internal.Shared.text +none = Html.Internal.Shared.none diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast index 0853b0e4f50..b3777d54930 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast @@ -38,11 +38,12 @@ Full { Slice(start = 0, length = 2), ], space_after: [ - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc deleted file mode 100644 index 2c6e02bbfd3..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc +++ /dev/null @@ -1,7 +0,0 @@ -# comment 1 -foo = 1 - -# comment 2 -bar = "hi" -baz = "stuff" -# comment n diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast index 87c2c1ae425..aa86b9b304a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast @@ -64,7 +64,7 @@ Defs { @26-27 Identifier { ident: "x", }, - @26-39 Apply( + @29-39 Apply( @29-32 TaskAwaitBang( Var { module_name: "B", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc index acc4f596f42..831a23396ae 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc @@ -7,9 +7,11 @@ import cli.Stdout main = # is this a valid statement? "Foo" |> A.x! + # what about this? "Bar" |> B.y! { config: "config" } C.z "Bar" + diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast index 8cf6d157d87..6f8c31abc05 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast @@ -46,17 +46,19 @@ Full { ], space_before: [ Slice(start = 0, length = 2), - Slice(start = 4, length = 0), + Slice(start = 2, length = 2), ], space_after: [ - Slice(start = 2, length = 2), - Slice(start = 4, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 2), ], spaces: [ Newline, Newline, Newline, Newline, + Newline, + Newline, ], type_defs: [], value_defs: [ @@ -88,22 +90,28 @@ Full { Index(2147483649), ], regions: [ - @120-132, + @120-133, @162-205, ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 0, length = 0), + Slice(start = 0, length = 3), ], space_after: [ Slice(start = 0, length = 0), - Slice(start = 0, length = 0), + Slice(start = 3, length = 0), + ], + spaces: [ + Newline, + Newline, + LineComment( + " what about this?", + ), ], - spaces: [], type_defs: [], value_defs: [ Stmt( - @120-132 BinOps( + @120-133 BinOps( [ ( @120-125 Str( @@ -123,55 +131,46 @@ Full { ), ), Stmt( - @162-205 SpaceBefore( - BinOps( - [ - ( - @162-167 Str( - PlainLine( - "Bar", - ), + @162-205 BinOps( + [ + ( + @162-167 Str( + PlainLine( + "Bar", ), - @168-170 Pizza, ), - ], - @171-205 Apply( - @171-174 TaskAwaitBang( - Var { - module_name: "B", - ident: "y", - }, - ), - [ - @185-205 SpaceBefore( - Record( - [ - @187-203 RequiredValue( - @187-193 "config", - [], - @195-203 Str( - PlainLine( - "config", - ), + @168-170 Pizza, + ), + ], + @171-205 Apply( + @171-174 TaskAwaitBang( + Var { + module_name: "B", + ident: "y", + }, + ), + [ + @185-205 SpaceBefore( + Record( + [ + @187-203 RequiredValue( + @187-193 "config", + [], + @195-203 Str( + PlainLine( + "config", ), ), - ], - ), - [ - Newline, + ), ], ), - ], - Space, - ), + [ + Newline, + ], + ), + ], + Space, ), - [ - Newline, - Newline, - LineComment( - " what about this?", - ), - ], ), ), ], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast index 21d5d8e5152..c4e24e09b64 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast @@ -52,11 +52,12 @@ Full { Slice(start = 0, length = 2), ], space_after: [ - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast index 7490de4952a..d8081869972 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast @@ -1,48 +1,53 @@ SpaceBefore( - Backpassing( - [ - @18-19 Identifier { - ident: "x", - }, - ], - @23-32 ParensAround( - Closure( - [ - @25-26 Identifier { - ident: "y", - }, - ], - @30-31 Var { - module_name: "", - ident: "y", + SpaceAfter( + Backpassing( + [ + @18-19 Identifier { + ident: "x", }, - ), - ), - @33-43 SpaceBefore( - Backpassing( - [ - @33-34 Identifier { - ident: "z", - }, - ], - @38-40 Record( - [], - ), - @42-43 SpaceBefore( - Var { + ], + @23-32 ParensAround( + Closure( + [ + @25-26 Identifier { + ident: "y", + }, + ], + @30-31 Var { module_name: "", - ident: "x", + ident: "y", }, + ), + ), + @33-43 SpaceBefore( + Backpassing( [ - Newline, - Newline, + @33-34 Identifier { + ident: "z", + }, ], + @38-40 Record( + [], + ), + @42-43 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast index 7cab905c510..f2661ac2048 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast @@ -1,44 +1,49 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @11-13 SpaceBefore( - StrLiteral( - PlainLine( - "", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-13 SpaceBefore( + StrLiteral( + PlainLine( + "", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @17-18 Num( + "1", ), - ], - value: @17-18 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @20-26 SpaceBefore( - StrLiteral( - PlainLine( - "mise", + guard: None, + }, + WhenBranch { + patterns: [ + @20-26 SpaceBefore( + StrLiteral( + PlainLine( + "mise", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @30-31 Num( + "2", ), - ], - value: @30-31 Num( - "2", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc new file mode 100644 index 00000000000..9d573d1810b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc @@ -0,0 +1,5 @@ +# leading comment +x = 5 +y = 6 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast index e306d5d4957..1980a11e660 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast @@ -1,54 +1,59 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @18-23, - @24-29, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @18-19 Identifier { - ident: "x", - }, - @22-23 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @18-23, + @24-29, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @18-19 Identifier { + ident: "x", + }, + @22-23 Num( + "5", + ), ), - ), - Body( - @24-25 Identifier { - ident: "y", - }, - @28-29 Num( - "6", + Body( + @24-25 Identifier { + ident: "y", + }, + @28-29 Num( + "6", + ), ), + ], + }, + @31-33 SpaceBefore( + Num( + "42", ), - ], - }, - @31-33 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast index 9c957001dc8..abdddd45266 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast @@ -1,32 +1,37 @@ SpaceBefore( - Backpassing( - [ - @18-19 Underscore( - "", - ), - ], - @23-32 ParensAround( - Closure( - [ - @25-26 Identifier { + SpaceAfter( + Backpassing( + [ + @18-19 Underscore( + "", + ), + ], + @23-32 ParensAround( + Closure( + [ + @25-26 Identifier { + ident: "y", + }, + ], + @30-31 Var { + module_name: "", ident: "y", }, - ], - @30-31 Var { - module_name: "", - ident: "y", - }, + ), ), - ), - @34-35 SpaceBefore( - Num( - "4", + @34-35 SpaceBefore( + Num( + "4", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast index 247fe016c7a..21945832304 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast @@ -1,231 +1,236 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), - Index(2147483651), - Index(2147483652), - ], - regions: [ - @0-19, - @20-39, - @40-59, - @60-72, - @73-128, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 1), - Slice(start = 2, length = 1), - Slice(start = 3, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - Slice(start = 4, length = 0), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Apply( - @0-4 Tag( - "Pair", - ), - [ - @5-6 Identifier { - ident: "x", - }, - @7-8 Underscore( - "", - ), - ], - ), - @11-19 Apply( - @11-15 Tag( - "Pair", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + Index(2147483651), + Index(2147483652), + ], + regions: [ + @0-19, + @20-39, + @40-59, + @60-72, + @73-128, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + Slice(start = 2, length = 1), + Slice(start = 3, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-8 Apply( + @0-4 Tag( + "Pair", + ), + [ + @5-6 Identifier { + ident: "x", + }, + @7-8 Underscore( + "", + ), + ], ), - [ - @16-17 Num( - "0", - ), - @18-19 Num( - "1", + @11-19 Apply( + @11-15 Tag( + "Pair", ), - ], - Space, - ), - ), - Body( - @20-28 Apply( - @20-24 Tag( - "Pair", + [ + @16-17 Num( + "0", + ), + @18-19 Num( + "1", + ), + ], + Space, ), - [ - @25-26 Underscore( - "", - ), - @27-28 Identifier { - ident: "y", - }, - ], ), - @31-39 Apply( - @31-35 Tag( - "Pair", + Body( + @20-28 Apply( + @20-24 Tag( + "Pair", + ), + [ + @25-26 Underscore( + "", + ), + @27-28 Identifier { + ident: "y", + }, + ], ), - [ - @36-37 Num( - "0", - ), - @38-39 Num( - "1", + @31-39 Apply( + @31-35 Tag( + "Pair", ), - ], - Space, - ), - ), - Body( - @40-48 Apply( - @40-44 Tag( - "Pair", + [ + @36-37 Num( + "0", + ), + @38-39 Num( + "1", + ), + ], + Space, ), - [ - @45-46 Underscore( - "", - ), - @47-48 Underscore( - "", - ), - ], ), - @51-59 Apply( - @51-55 Tag( - "Pair", + Body( + @40-48 Apply( + @40-44 Tag( + "Pair", + ), + [ + @45-46 Underscore( + "", + ), + @47-48 Underscore( + "", + ), + ], ), - [ - @56-57 Num( - "0", - ), - @58-59 Num( - "1", + @51-59 Apply( + @51-55 Tag( + "Pair", ), - ], - Space, - ), - ), - Body( - @60-61 Underscore( - "", - ), - @64-72 Apply( - @64-68 Tag( - "Pair", + [ + @56-57 Num( + "0", + ), + @58-59 Num( + "1", + ), + ], + Space, ), - [ - @69-70 Num( - "0", - ), - @71-72 Num( - "1", - ), - ], - Space, ), - ), - Body( - @73-98 Apply( - @73-77 Tag( - "Pair", + Body( + @60-61 Underscore( + "", ), - [ - @79-87 Apply( - @79-83 Tag( - "Pair", - ), - [ - @84-85 Identifier { - ident: "x", - }, - @86-87 Underscore( - "", - ), - ], + @64-72 Apply( + @64-68 Tag( + "Pair", ), - @90-98 Apply( - @90-94 Tag( - "Pair", + [ + @69-70 Num( + "0", ), - [ - @95-96 Underscore( - "", - ), - @97-98 Identifier { - ident: "y", - }, - ], - ), - ], - ), - @102-128 Apply( - @102-106 Tag( - "Pair", + @71-72 Num( + "1", + ), + ], + Space, ), - [ - @108-116 ParensAround( - Apply( - @108-112 Tag( + ), + Body( + @73-98 Apply( + @73-77 Tag( + "Pair", + ), + [ + @79-87 Apply( + @79-83 Tag( "Pair", ), [ - @113-114 Num( - "0", - ), - @115-116 Num( - "1", + @84-85 Identifier { + ident: "x", + }, + @86-87 Underscore( + "", ), ], - Space, ), - ), - @119-127 ParensAround( - Apply( - @119-123 Tag( + @90-98 Apply( + @90-94 Tag( "Pair", ), [ - @124-125 Num( - "2", - ), - @126-127 Num( - "3", + @95-96 Underscore( + "", ), + @97-98 Identifier { + ident: "y", + }, ], - Space, ), - ), - ], - Space, + ], + ), + @102-128 Apply( + @102-106 Tag( + "Pair", + ), + [ + @108-116 ParensAround( + Apply( + @108-112 Tag( + "Pair", + ), + [ + @113-114 Num( + "0", + ), + @115-116 Num( + "1", + ), + ], + Space, + ), + ), + @119-127 ParensAround( + Apply( + @119-123 Tag( + "Pair", + ), + [ + @124-125 Num( + "2", + ), + @126-127 Num( + "3", + ), + ], + Space, + ), + ), + ], + Space, + ), ), + ], + }, + @130-131 SpaceBefore( + Num( + "0", ), - ], - }, - @130-131 SpaceBefore( - Num( - "0", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc new file mode 100644 index 00000000000..a58ff1f7d1f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc @@ -0,0 +1,9 @@ +when x is + _ -> + 1 + + _ -> + 2 + + Ok -> + 3 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast index bdd18adf88f..4d479df9362 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast @@ -1,73 +1,78 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @14-15 SpaceBefore( - Underscore( - "", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-15 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], + ), + ], + value: @27-28 SpaceBefore( + Num( + "1", ), [ Newline, ], ), - ], - value: @27-28 SpaceBefore( - Num( - "1", - ), - [ - Newline, + guard: None, + }, + WhenBranch { + patterns: [ + @34-35 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + Newline, + ], + ), ], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @34-35 SpaceBefore( - Underscore( - "", + value: @47-48 SpaceBefore( + Num( + "2", ), [ Newline, - Newline, ], ), - ], - value: @47-48 SpaceBefore( - Num( - "2", - ), - [ - Newline, + guard: None, + }, + WhenBranch { + patterns: [ + @54-56 SpaceBefore( + Tag( + "Ok", + ), + [ + Newline, + Newline, + ], + ), ], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @54-56 SpaceBefore( - Tag( - "Ok", + value: @68-69 SpaceBefore( + Num( + "3", ), [ Newline, - Newline, ], ), - ], - value: @68-69 SpaceBefore( - Num( - "3", - ), - [ - Newline, - ], - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast index 2844887e56d..4f6485e83ac 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast @@ -1,31 +1,36 @@ -ParensAround( - When( - @6-7 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @15-17 SpaceBefore( - Tag( - "Ok", +SpaceAfter( + ParensAround( + When( + @6-7 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @15-17 SpaceBefore( + Tag( + "Ok", + ), + [ + Newline, + ], + ), + ], + value: @29-30 SpaceBefore( + Num( + "3", ), [ Newline, ], ), - ], - value: @29-30 SpaceBefore( - Num( - "3", - ), - [ - Newline, - ], - ), - guard: None, - }, - ], + guard: None, + }, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast index 419e8c07ad9..f9ce146b75a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast @@ -1,31 +1,36 @@ -ParensAround( - SpaceAfter( - When( - @6-7 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @15-17 SpaceBefore( - Tag( - "Ok", +SpaceAfter( + ParensAround( + SpaceAfter( + When( + @6-7 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @15-17 SpaceBefore( + Tag( + "Ok", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @21-22 Num( + "3", ), - ], - value: @21-22 Num( - "3", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast index 2b573943f62..99e20082185 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast @@ -1,87 +1,92 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @11-17 SpaceBefore( - StrLiteral( - PlainLine( - "blah", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-17 SpaceBefore( + StrLiteral( + PlainLine( + "blah", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @20-26 StrLiteral( - PlainLine( - "blop", - ), - ), - ], - value: @30-31 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @33-38 SpaceBefore( - StrLiteral( + @20-26 StrLiteral( PlainLine( - "foo", + "blop", ), ), - [ - Newline, - ], + ], + value: @30-31 Num( + "1", ), - @43-48 SpaceBefore( - SpaceAfter( + guard: None, + }, + WhenBranch { + patterns: [ + @33-38 SpaceBefore( StrLiteral( PlainLine( - "bar", + "foo", ), ), [ Newline, ], ), - [ - Newline, - ], - ), - @51-56 StrLiteral( - PlainLine( - "baz", + @43-48 SpaceBefore( + SpaceAfter( + StrLiteral( + PlainLine( + "bar", + ), + ), + [ + Newline, + ], + ), + [ + Newline, + ], ), - ), - ], - value: @60-61 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @63-70 SpaceBefore( - StrLiteral( + @51-56 StrLiteral( PlainLine( - "stuff", + "baz", ), ), - [ - Newline, - ], + ], + value: @60-61 Num( + "2", ), - ], - value: @74-75 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + WhenBranch { + patterns: [ + @63-70 SpaceBefore( + StrLiteral( + PlainLine( + "stuff", + ), + ), + [ + Newline, + ], + ), + ], + value: @74-75 Num( + "4", + ), + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast index 1822dfa9627..9944ab2eff2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast @@ -1,54 +1,59 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @14-15 SpaceBefore( - NumLiteral( - "1", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-15 SpaceBefore( + NumLiteral( + "1", + ), + [ + Newline, + ], ), + ], + value: @19-33 Apply( + @19-26 Var { + module_name: "Num", + ident: "neg", + }, [ - Newline, + @32-33 SpaceBefore( + Num( + "2", + ), + [ + Newline, + ], + ), ], + Space, ), - ], - value: @19-33 Apply( - @19-26 Var { - module_name: "Num", - ident: "neg", - }, - [ - @32-33 SpaceBefore( - Num( - "2", + guard: None, + }, + WhenBranch { + patterns: [ + @38-39 SpaceBefore( + Underscore( + "", ), [ Newline, ], ), ], - Space, - ), - guard: None, - }, - WhenBranch { - patterns: [ - @38-39 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - ], + value: @43-44 Num( + "4", ), - ], - value: @43-44 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast index 8c6a5e4e61b..30c773b5fc1 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @11-12 SpaceBefore( - NumLiteral( - "1", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-12 SpaceBefore( + NumLiteral( + "1", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @16-17 Num( + "2", ), - ], - value: @16-17 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @19-21 SpaceBefore( - NumLiteral( - "-3", + guard: None, + }, + WhenBranch { + patterns: [ + @19-21 SpaceBefore( + NumLiteral( + "-3", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @25-26 Num( + "4", ), - ], - value: @25-26 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast index 8866aef903c..30f11b37812 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @11-12 SpaceBefore( - NumLiteral( - "1", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-12 SpaceBefore( + NumLiteral( + "1", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @16-17 Num( + "2", ), - ], - value: @16-17 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @19-20 SpaceBefore( - NumLiteral( - "3", + guard: None, + }, + WhenBranch { + patterns: [ + @19-20 SpaceBefore( + NumLiteral( + "3", + ), + [ + Newline, + ], ), - [ - Newline, - ], + ], + value: @24-25 Num( + "4", ), - ], - value: @24-25 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast index 33e249c2a28..deb3e72ddd7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast @@ -1,51 +1,56 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @11-16 SpaceBefore( - RecordDestructure( +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-16 SpaceBefore( + RecordDestructure( + [ + @13-14 Identifier { + ident: "y", + }, + ], + ), [ - @13-14 Identifier { - ident: "y", - }, + Newline, ], ), - [ - Newline, - ], + ], + value: @20-21 Num( + "2", ), - ], - value: @20-21 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @23-31 SpaceBefore( - RecordDestructure( + guard: None, + }, + WhenBranch { + patterns: [ + @23-31 SpaceBefore( + RecordDestructure( + [ + @25-26 Identifier { + ident: "z", + }, + @28-29 Identifier { + ident: "w", + }, + ], + ), [ - @25-26 Identifier { - ident: "z", - }, - @28-29 Identifier { - ident: "w", - }, + Newline, ], ), - [ - Newline, - ], + ], + value: @35-36 Num( + "4", ), - ], - value: @35-36 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast index b36a5548545..e7092abefe0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast @@ -1,94 +1,99 @@ -When( - @5-18 Record( - [ - @6-17 RequiredValue( - @6-9 "foo", - [], - @11-17 Tuple( - [ - @12-13 Num( - "1", - ), - @15-16 Num( - "2", - ), - ], - ), - ), - ], - ), - [ - WhenBranch { - patterns: [ - @23-36 SpaceBefore( - RecordDestructure( +SpaceAfter( + When( + @5-18 Record( + [ + @6-17 RequiredValue( + @6-9 "foo", + [], + @11-17 Tuple( [ - @24-35 RequiredField( - "foo", - @29-35 Tuple( - [ - @30-31 NumLiteral( - "1", - ), - @33-34 Identifier { - ident: "x", - }, - ], - ), + @12-13 Num( + "1", + ), + @15-16 Num( + "2", ), ], ), - [ - Newline, - ], ), ], - value: @40-41 Var { - module_name: "", - ident: "x", - }, - guard: None, - }, - WhenBranch { - patterns: [ - @43-56 SpaceBefore( - RecordDestructure( + ), + [ + WhenBranch { + patterns: [ + @23-36 SpaceBefore( + RecordDestructure( + [ + @24-35 RequiredField( + "foo", + @29-35 Tuple( + [ + @30-31 NumLiteral( + "1", + ), + @33-34 Identifier { + ident: "x", + }, + ], + ), + ), + ], + ), [ - @44-55 RequiredField( - "foo", - @49-55 Tuple( - [ - @50-51 Underscore( - "", - ), - @53-54 Identifier { - ident: "b", - }, - ], + Newline, + ], + ), + ], + value: @40-41 Var { + module_name: "", + ident: "x", + }, + guard: None, + }, + WhenBranch { + patterns: [ + @43-56 SpaceBefore( + RecordDestructure( + [ + @44-55 RequiredField( + "foo", + @49-55 Tuple( + [ + @50-51 Underscore( + "", + ), + @53-54 Identifier { + ident: "b", + }, + ], + ), ), - ), + ], + ), + [ + Newline, ], ), + ], + value: @60-65 BinOps( [ - Newline, + ( + @60-61 Num( + "3", + ), + @62-63 Plus, + ), ], + @64-65 Var { + module_name: "", + ident: "b", + }, ), - ], - value: @60-65 BinOps( - [ - ( - @60-61 Num( - "3", - ), - @62-63 Plus, - ), - ], - @64-65 Var { - module_name: "", - ident: "b", - }, - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast index 687ef1ac7c0..3c1698072bf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast @@ -1,72 +1,77 @@ -When( - @5-11 Tuple( +SpaceAfter( + When( + @5-11 Tuple( + [ + @6-7 Num( + "1", + ), + @9-10 Num( + "2", + ), + ], + ), [ - @6-7 Num( - "1", - ), - @9-10 Num( - "2", - ), - ], - ), - [ - WhenBranch { - patterns: [ - @16-22 SpaceBefore( - Tuple( + WhenBranch { + patterns: [ + @16-22 SpaceBefore( + Tuple( + [ + @17-18 NumLiteral( + "1", + ), + @20-21 Identifier { + ident: "x", + }, + ], + ), [ - @17-18 NumLiteral( - "1", - ), - @20-21 Identifier { - ident: "x", - }, + Newline, ], ), - [ - Newline, - ], - ), - ], - value: @26-27 Var { - module_name: "", - ident: "x", + ], + value: @26-27 Var { + module_name: "", + ident: "x", + }, + guard: None, }, - guard: None, - }, - WhenBranch { - patterns: [ - @29-35 SpaceBefore( - Tuple( + WhenBranch { + patterns: [ + @29-35 SpaceBefore( + Tuple( + [ + @30-31 Underscore( + "", + ), + @33-34 Identifier { + ident: "b", + }, + ], + ), [ - @30-31 Underscore( - "", - ), - @33-34 Identifier { - ident: "b", - }, + Newline, ], ), + ], + value: @39-44 BinOps( [ - Newline, + ( + @39-40 Num( + "3", + ), + @41-42 Plus, + ), ], + @43-44 Var { + module_name: "", + ident: "b", + }, ), - ], - value: @39-44 BinOps( - [ - ( - @39-40 Num( - "3", - ), - @41-42 Plus, - ), - ], - @43-44 Var { - module_name: "", - ident: "b", - }, - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc new file mode 100644 index 00000000000..8aecb8257fc --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc @@ -0,0 +1,3 @@ +f : a -> (b -> c) where a implements A + +f \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast index d2ddc4d1bda..dcdf36e2695 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast @@ -1,66 +1,71 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-38, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-38 Where( - @4-16 Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @10-16 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-38, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-38 Where( + @4-16 Function( [ - @10-11 BoundVariable( - "b", + @4-5 BoundVariable( + "a", ), ], - @15-16 BoundVariable( - "c", + @10-16 Function( + [ + @10-11 BoundVariable( + "b", + ), + ], + @15-16 BoundVariable( + "c", + ), ), ), + [ + @24-38 ImplementsClause { + var: @24-25 "a", + abilities: [ + @37-38 Apply( + "", + "A", + [], + ), + ], + }, + ], ), - [ - @24-38 ImplementsClause { - var: @24-25 "a", - abilities: [ - @37-38 Apply( - "", - "A", - [], - ), - ], - }, - ], ), - ), - ], - }, - @40-41 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @40-41 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast index a0e3798f038..8df0cb9b007 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast @@ -1,155 +1,160 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @0-73, - @75-154, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-73 Where( - @4-10 Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @9-10 BoundVariable( - "b", - ), - ), - [ - @17-39 ImplementsClause { - var: @17-18 "a", - abilities: [ - @30-34 Apply( - "", - "Hash", - [], - ), - @37-39 Apply( - "", - "Eq", - [], - ), - ], - }, - @41-73 ImplementsClause { - var: @41-42 "b", - abilities: [ - @54-56 Apply( - "", - "Eq", - [], - ), - @59-63 Apply( - "", - "Hash", - [], - ), - @66-73 Apply( - "", - "Display", - [], - ), - ], - }, - ], - ), - ), - Annotation( - @75-76 Identifier { - ident: "f", - }, - @79-154 Where( - @79-85 SpaceAfter( - Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @0-73, + @75-154, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-73 Where( + @4-10 Function( [ - @79-80 BoundVariable( + @4-5 BoundVariable( "a", ), ], - @84-85 BoundVariable( + @9-10 BoundVariable( "b", ), ), [ - Newline, + @17-39 ImplementsClause { + var: @17-18 "a", + abilities: [ + @30-34 Apply( + "", + "Hash", + [], + ), + @37-39 Apply( + "", + "Eq", + [], + ), + ], + }, + @41-73 ImplementsClause { + var: @41-42 "b", + abilities: [ + @54-56 Apply( + "", + "Eq", + [], + ), + @59-63 Apply( + "", + "Hash", + [], + ), + @66-73 Apply( + "", + "Display", + [], + ), + ], + }, ], ), - [ - @94-116 ImplementsClause { - var: @94-95 "a", - abilities: [ - @107-111 Apply( - "", - "Hash", - [], - ), - @114-116 Apply( - "", - "Eq", - [], - ), - ], - }, - @122-154 ImplementsClause { - var: @122-123 SpaceBefore( - "b", + ), + Annotation( + @75-76 Identifier { + ident: "f", + }, + @79-154 Where( + @79-85 SpaceAfter( + Function( [ - Newline, + @79-80 BoundVariable( + "a", + ), ], - ), - abilities: [ - @135-139 Apply( - "", - "Hash", - [], - ), - @142-149 Apply( - "", - "Display", - [], - ), - @152-154 Apply( - "", - "Eq", - [], + @84-85 BoundVariable( + "b", ), + ), + [ + Newline, ], - }, - ], + ), + [ + @94-116 ImplementsClause { + var: @94-95 "a", + abilities: [ + @107-111 Apply( + "", + "Hash", + [], + ), + @114-116 Apply( + "", + "Eq", + [], + ), + ], + }, + @122-154 ImplementsClause { + var: @122-123 SpaceBefore( + "b", + [ + Newline, + ], + ), + abilities: [ + @135-139 Apply( + "", + "Hash", + [], + ), + @142-149 Apply( + "", + "Display", + [], + ), + @152-154 Apply( + "", + "Eq", + [], + ), + ], + }, + ], + ), ), - ), - ], - }, - @156-157 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @156-157 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc new file mode 100644 index 00000000000..e8f053233ac --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc @@ -0,0 +1,3 @@ +f : a -> (b -> c) where a implements A, b implements Eq, c implements Ord + +f \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast index a900672d365..7a57a5fe01b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast @@ -1,86 +1,91 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-73, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-73 Where( - @4-16 Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @10-16 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-73, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-73 Where( + @4-16 Function( [ - @10-11 BoundVariable( - "b", + @4-5 BoundVariable( + "a", ), ], - @15-16 BoundVariable( - "c", + @10-16 Function( + [ + @10-11 BoundVariable( + "b", + ), + ], + @15-16 BoundVariable( + "c", + ), ), ), + [ + @24-38 ImplementsClause { + var: @24-25 "a", + abilities: [ + @37-38 Apply( + "", + "A", + [], + ), + ], + }, + @40-55 ImplementsClause { + var: @40-41 "b", + abilities: [ + @53-55 Apply( + "", + "Eq", + [], + ), + ], + }, + @57-73 ImplementsClause { + var: @57-58 "c", + abilities: [ + @70-73 Apply( + "", + "Ord", + [], + ), + ], + }, + ], ), - [ - @24-38 ImplementsClause { - var: @24-25 "a", - abilities: [ - @37-38 Apply( - "", - "A", - [], - ), - ], - }, - @40-55 ImplementsClause { - var: @40-41 "b", - abilities: [ - @53-55 Apply( - "", - "Eq", - [], - ), - ], - }, - @57-73 ImplementsClause { - var: @57-58 "c", - abilities: [ - @70-73 Apply( - "", - "Ord", - [], - ), - ], - }, - ], ), - ), - ], - }, - @75-76 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @75-76 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast index 69fa4993365..86b323a124f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast @@ -1,101 +1,106 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-92, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-92 Where( - @4-16 SpaceAfter( - Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @10-16 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-92, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-92 Where( + @4-16 SpaceAfter( + Function( [ - @10-11 BoundVariable( - "b", + @4-5 BoundVariable( + "a", ), ], - @15-16 BoundVariable( - "c", + @10-16 Function( + [ + @10-11 BoundVariable( + "b", + ), + ], + @15-16 BoundVariable( + "c", + ), ), ), + [ + Newline, + ], ), [ - Newline, - ], - ), - [ - @28-45 ImplementsClause { - var: @28-29 "a", - abilities: [ - @41-45 Apply( - "", - "Hash", - [], - ), - ], - }, - @53-68 ImplementsClause { - var: @53-54 SpaceBefore( - "b", - [ - Newline, + @28-45 ImplementsClause { + var: @28-29 "a", + abilities: [ + @41-45 Apply( + "", + "Hash", + [], + ), ], - ), - abilities: [ - @66-68 Apply( - "", - "Eq", - [], + }, + @53-68 ImplementsClause { + var: @53-54 SpaceBefore( + "b", + [ + Newline, + ], ), - ], - }, - @76-92 ImplementsClause { - var: @76-77 SpaceBefore( - "c", - [ - Newline, + abilities: [ + @66-68 Apply( + "", + "Eq", + [], + ), ], - ), - abilities: [ - @89-92 Apply( - "", - "Ord", - [], + }, + @76-92 ImplementsClause { + var: @76-77 SpaceBefore( + "c", + [ + Newline, + ], ), - ], - }, - ], + abilities: [ + @89-92 Apply( + "", + "Ord", + [], + ), + ], + }, + ], + ), ), - ), - ], - }, - @94-95 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @94-95 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc new file mode 100644 index 00000000000..a175ef55b37 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc @@ -0,0 +1,3 @@ +f : a where a implements A + +f \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast index c1ae8292a27..8c99195e4de 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast @@ -1,52 +1,57 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-26 Where( - @4-5 BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-26, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-26 Where( + @4-5 BoundVariable( + "a", + ), + [ + @12-26 ImplementsClause { + var: @12-13 "a", + abilities: [ + @25-26 Apply( + "", + "A", + [], + ), + ], + }, + ], ), - [ - @12-26 ImplementsClause { - var: @12-13 "a", - abilities: [ - @25-26 Apply( - "", - "A", - [], - ), - ], - }, - ], ), - ), - ], - }, - @28-29 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @28-29 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast index bbe411ff15e..c60a3cad6da 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast @@ -1,66 +1,71 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-40, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-40 Where( - @4-12 SpaceAfter( - Function( - [ - @4-5 BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-40, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-40 Where( + @4-12 SpaceAfter( + Function( + [ + @4-5 BoundVariable( + "a", + ), + ], + @9-12 Apply( + "", + "U64", + [], ), - ], - @9-12 Apply( - "", - "U64", - [], ), + [ + Newline, + ], ), [ - Newline, + @23-40 ImplementsClause { + var: @23-24 "a", + abilities: [ + @36-40 Apply( + "", + "Hash", + [], + ), + ], + }, ], ), - [ - @23-40 ImplementsClause { - var: @23-24 "a", - abilities: [ - @36-40 Apply( - "", - "Hash", - [], - ), - ], - }, - ], ), - ), - ], - }, - @42-43 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @42-43 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast index b362ed0e8d7..06fa1daab46 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast @@ -1,67 +1,72 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-39, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-5 Identifier { - ident: "where", - }, - ann_type: @8-20 Record { - fields: [ - @9-19 RequiredValue( - @9-14 "where", - [], - @16-19 Apply( - "", - "I32", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-39, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-5 Identifier { + ident: "where", + }, + ann_type: @8-20 Record { + fields: [ + @9-19 RequiredValue( + @9-14 "where", [], + @16-19 Apply( + "", + "I32", + [], + ), ), - ), - ], - ext: None, + ], + ext: None, + }, + comment: None, + body_pattern: @21-26 Identifier { + ident: "where", + }, + body_expr: @29-39 Record( + [ + @30-38 RequiredValue( + @30-35 "where", + [], + @37-38 Num( + "1", + ), + ), + ], + ), }, - comment: None, - body_pattern: @21-26 Identifier { + ], + }, + @41-52 SpaceBefore( + RecordAccess( + Var { + module_name: "", ident: "where", }, - body_expr: @29-39 Record( - [ - @30-38 RequiredValue( - @30-35 "where", - [], - @37-38 Num( - "1", - ), - ), - ], - ), - }, - ], - }, - @41-52 SpaceBefore( - RecordAccess( - Var { - module_name: "", - ident: "where", - }, - "where", + "where", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 8691f589cbc..683084b6f40 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -42,9 +42,16 @@ mod test_fmt { Ok(loc_defs) => { fmt_defs(buf, &loc_defs, 0); } - Err(error) => panic!( - r"Unexpected parse failure when parsing this for defs formatting:\n\n{src:?}\n\nParse error was:\n\n{error:?}\n\n" - ), + Err(error) => { + let src = if src.len() > 1000 { + "" + } else { + src + }; + panic!( + "Unexpected parse failure when parsing this for defs formatting:\n\n{src}\n\nParse error was:\n\n{error:?}\n\n" + ) + } } } @@ -56,7 +63,7 @@ mod test_fmt { match module::parse_header(&arena, State::new(src.as_bytes())) { Ok((actual, state)) => { - use roc_fmt::spaces::RemoveSpaces; + use roc_parse::remove_spaces::RemoveSpaces; let mut buf = Buf::new_in(&arena); @@ -3923,6 +3930,7 @@ mod test_fmt { } #[test] + #[ignore] fn with_multiline_pattern_indentation() { expr_formats_to( indoc!( @@ -5434,73 +5442,6 @@ mod test_fmt { )); } - #[test] - fn backpassing_parens_body() { - expr_formats_same(indoc!( - r" - Task.fromResult - ( - b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - )); - - expr_formats_to( - indoc!( - r" - Task.fromResult - (b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - ), - indoc!( - r" - Task.fromResult - ( - b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - ), - ); - - expr_formats_to( - indoc!( - r" - Task.fromResult - (b <- binaryOp ctx - if a == b then - -1 - else - 0) - " - ), - indoc!( - r" - Task.fromResult - ( - b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - ), - ); - } - #[test] fn backpassing_body_on_newline() { expr_formats_same(indoc!( diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 59bd318ccc9..c4ceb8de79e 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -22,16 +22,16 @@ mod test_snapshots { macro_rules! snapshot_input { (expr => $input:expr) => { - Input::Expr($input.trim()) + Input::Expr($input) }; (header => $input:expr) => { - Input::Header($input.trim()) + Input::Header($input) }; (moduledefs => $input:expr) => { - Input::ModuleDefs($input.trim()) + Input::ModuleDefs($input) }; (full => $input:expr) => { - Input::Full($input.trim()) + Input::Full($input) }; } @@ -187,8 +187,11 @@ mod test_snapshots { fail/ability_first_demand_not_indented_enough.expr, fail/ability_non_signature_expression.expr, fail/alias_or_opaque_fail.expr, + fail/backpassing_after_annotation.expr, + fail/bound_variable.expr, fail/comment_with_tab.expr, fail/def_missing_final_expression.expr, + fail/def_without_newline.expr, fail/double_plus.expr, fail/elm_function_syntax.expr, fail/empty_or_pattern.expr, @@ -215,6 +218,8 @@ mod test_snapshots { fail/module_params_with_missing_arrow.header, fail/module_with_unfinished_params.header, fail/multi_no_end.expr, + fail/newline_before_operator_with_defs.expr, + fail/opaque_type_def_with_newline.expr, fail/pattern_binds_keyword.expr, fail/pattern_in_parens_end.expr, fail/pattern_in_parens_end_comma.expr, @@ -243,6 +248,7 @@ mod test_snapshots { fail/type_inline_alias.expr, fail/underscore_name_type_annotation.expr, fail/unfinished_closure_pattern_in_parens.expr, + fail/unfinished_import_as_or_exposing.moduledefs, fail/unicode_not_hex.expr, fail/weird_escape.expr, fail/when_missing_arrow.expr, @@ -281,16 +287,13 @@ mod test_snapshots { pass/basic_tag.expr, pass/basic_tuple.expr, pass/basic_var.expr, - pass/bound_variable.expr, - pass/call_with_newlines.expr, - pass/closure_in_binop.expr, pass/closure_in_binop_with_spaces.expr, pass/closure_with_underscores.expr, + pass/comma_prefixed_indented_record.expr, pass/comment_after_annotation.expr, pass/comment_after_def.moduledefs, pass/comment_after_expr_in_parens.expr, pass/comment_after_op.expr, - pass/comment_after_tag_in_def.expr, pass/comment_before_colon_def.expr, pass/comment_before_equals_def.expr, pass/comment_before_op.expr, @@ -300,7 +303,7 @@ mod test_snapshots { pass/crash.expr, pass/dbg.expr, pass/dbg_multiline.expr, - pass/def_without_newline.expr, + pass/defs_suffixed_middle_extra_indents.moduledefs, pass/destructure_tag_assignment.expr, pass/docs.expr, pass/empty_app_header.header, @@ -315,7 +318,10 @@ mod test_snapshots { pass/equals.expr, pass/equals_with_spaces.expr, pass/expect.expr, + pass/expect_defs.moduledefs, pass/expect_fx.moduledefs, + pass/expect_single_line.expr, + pass/extra_newline.expr, pass/extra_newline_in_parens.expr, pass/float_with_underscores.expr, pass/fn_with_record_arg.expr, @@ -333,6 +339,7 @@ mod test_snapshots { pass/import_with_comments.moduledefs, pass/import_with_exposed.moduledefs, pass/import_with_params.moduledefs, + pass/indented_after_multi_backpassing.expr, pass/ingested_file.moduledefs, pass/inline_import.expr, pass/inline_ingested_file.expr, @@ -362,6 +369,8 @@ mod test_snapshots { pass/multi_backpassing_in_def.moduledefs, pass/multi_backpassing_with_apply.expr, pass/multi_char_string.expr, + pass/multiline_binop_when_with_comments.expr, + pass/multiline_str_in_pat.expr, pass/multiline_string.expr, pass/multiline_string_in_apply.expr, pass/multiline_tuple_with_comments.expr, @@ -375,9 +384,7 @@ mod test_snapshots { pass/negative_float.expr, pass/negative_in_apply_def.expr, pass/negative_int.expr, - pass/nested_backpassing_no_newline_before.expr, pass/nested_def_annotation.moduledefs, - pass/nested_def_without_newline.expr, pass/nested_if.expr, pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51 pass/newline_after_mul.expr, @@ -385,7 +392,6 @@ mod test_snapshots { pass/newline_after_sub.expr, pass/newline_and_spaces_before_less_than.expr, pass/newline_before_add.expr, - pass/newline_before_operator_with_defs.expr, pass/newline_before_sub.expr, pass/newline_in_packages.full, pass/newline_in_type_alias_application.expr, @@ -412,7 +418,6 @@ mod test_snapshots { pass/opaque_reference_pattern.expr, pass/opaque_reference_pattern_with_arguments.expr, pass/opaque_simple.moduledefs, - pass/opaque_type_def_with_newline.expr, pass/opaque_with_type_arguments.moduledefs, pass/ops_with_newlines.expr, pass/outdented_app_with_record.expr, @@ -421,7 +426,6 @@ mod test_snapshots { pass/outdented_record.expr, pass/packed_singleton_list.expr, pass/parens_in_type_def_apply.expr, - pass/parens_in_value_def_annotation.expr, pass/parenthesized_type_def.expr, pass/parenthesized_type_def_space_before.expr, pass/parenthetical_apply.expr, @@ -449,6 +453,7 @@ mod test_snapshots { pass/record_update.expr, pass/record_with_if.expr, pass/requires_type.header, + pass/separate_defs.moduledefs, pass/single_arg_closure.expr, pass/single_underscore_closure.expr, pass/space_before_colon.full, diff --git a/crates/repl_ui/src/lib.rs b/crates/repl_ui/src/lib.rs index 75cf256848d..105b0e78db4 100644 --- a/crates/repl_ui/src/lib.rs +++ b/crates/repl_ui/src/lib.rs @@ -7,7 +7,7 @@ use bumpalo::Bump; use colors::{CYAN, END_COL, GREEN}; use const_format::concatcp; use repl_state::{parse_src, ParseOutcome}; -use roc_parse::ast::{Expr, ValueDef}; +use roc_parse::ast::{Expr, ExtractSpaces, ValueDef}; use roc_repl_eval::gen::{Problems, ReplOutput}; use roc_reporting::report::StyleCodes; @@ -63,24 +63,30 @@ pub fn is_incomplete(input: &str) -> bool { match parse_src(&arena, input) { ParseOutcome::Incomplete => !input.ends_with('\n'), - // Standalone annotations are default incomplete, because we can't know - // whether they're about to annotate a body on the next line - // (or if not, meaning they stay standalone) until you press Enter again! - // - // So it's Incomplete until you've pressed Enter again (causing the input to end in "\n") - ParseOutcome::ValueDef(ValueDef::Annotation(_, _)) if !input.ends_with('\n') => true, - ParseOutcome::Expr(Expr::When(_, _)) => { - // There might be lots of `when` branches, so don't assume the user is done entering - // them until they enter a blank line! - !input.ends_with('\n') + ParseOutcome::DefsAndExpr(defs, None) => { + // Standalone annotations are default incomplete, because we can't know + // whether they're about to annotate a body on the next line + // (or if not, meaning they stay standalone) until you press Enter again! + // + // So it's Incomplete until you've pressed Enter again (causing the input to end in "\n") + if matches!(defs.last(), Some(Err(ValueDef::Annotation(_, _)))) { + !input.ends_with('\n') + } else { + false + } + } + ParseOutcome::DefsAndExpr(_, Some(expr)) => { + if matches!(expr.extract_spaces().item, Expr::When(..)) { + // There might be lots of `when` branches, so don't assume the user is done entering + // them until they enter a blank line! + !input.ends_with('\n') + } else { + false + } + } + ParseOutcome::Empty | ParseOutcome::Help | ParseOutcome::Exit | ParseOutcome::SyntaxErr => { + false } - ParseOutcome::Empty - | ParseOutcome::Help - | ParseOutcome::Exit - | ParseOutcome::ValueDef(_) - | ParseOutcome::TypeDef(_) - | ParseOutcome::SyntaxErr - | ParseOutcome::Expr(_) => false, } } diff --git a/crates/repl_ui/src/repl_state.rs b/crates/repl_ui/src/repl_state.rs index 2998fb29ace..2b612d832c2 100644 --- a/crates/repl_ui/src/repl_state.rs +++ b/crates/repl_ui/src/repl_state.rs @@ -4,13 +4,11 @@ use std::{fs, io}; use bumpalo::Bump; use roc_collections::MutSet; use roc_load::MonomorphizedModule; -use roc_parse::ast::{Expr, Pattern, StrLiteral, TypeDef, TypeHeader, ValueDef}; -use roc_parse::expr::{parse_single_def, ExprParseOptions, SingleDef}; -use roc_parse::parser::Parser; +use roc_parse::ast::{Defs, Expr, Pattern, StrLiteral, TypeDef, TypeHeader, ValueDef}; +use roc_parse::expr::parse_repl_defs_and_optional_expr; +use roc_parse::parser::EWhen; use roc_parse::parser::{EClosure, EExpr, EPattern}; -use roc_parse::parser::{EWhen, Either}; use roc_parse::state::State; -use roc_parse::{join_alias_to_body, join_ann_to_body}; use roc_region::all::Loc; use roc_repl_eval::gen::{compile_to_mono, Problems}; use roc_reporting::report::Palette; @@ -64,11 +62,11 @@ impl ReplState { target: Target, palette: Palette, ) -> ReplAction<'a> { - let pending_past_def; + let mut pending_past_def = None; let src: &str = match parse_src(arena, line) { ParseOutcome::Empty | ParseOutcome::Help => return ReplAction::Help, ParseOutcome::Exit => return ReplAction::Exit, - ParseOutcome::Expr(_) | ParseOutcome::Incomplete | ParseOutcome::SyntaxErr => { + ParseOutcome::Incomplete | ParseOutcome::SyntaxErr => { pending_past_def = None; // If it's a SyntaxErr (or Incomplete at this point, meaning it will @@ -76,149 +74,188 @@ impl ReplState { // proceed as normal and let the error reporting happen during eval. line } - ParseOutcome::ValueDef(value_def) => { - match value_def { - ValueDef::Annotation( - Loc { - // TODO is this right for suffixed - value: Pattern::Identifier { ident }, - .. - }, - _, - ) => { - // Record the standalone type annotation for future use. - self.add_past_def(ident.trim_end().to_string(), line.to_string()); - - // Return early without running eval, since standalone annotations - // cannot be evaluated as expressions. - return ReplAction::Nothing; - } - ValueDef::Body( - Loc { - // TODO is this right for suffixed - value: Pattern::Identifier { ident }, - .. - }, - _, - ) - | ValueDef::AnnotatedBody { - body_pattern: - Loc { - // TODO is this right for suffixed - value: Pattern::Identifier { ident }, - .. - }, - .. - } => { - pending_past_def = Some((ident.to_string(), line.to_string())); - - // Recreate the body of the def and then evaluate it as a lookup. - // We do this so that any errors will get reported as part of this expr; - // if we just did a lookup on the past def, then errors wouldn't get - // reported because we filter out errors whose regions are in past defs. - let mut buf = bumpalo::collections::string::String::with_capacity_in( - ident.len() + line.len() + 1, - arena, - ); - - buf.push_str(line); - buf.push('\n'); - buf.push_str(ident); - - buf.into_bump_str() - } - ValueDef::Annotation(_, _) - | ValueDef::Body(_, _) - | ValueDef::AnnotatedBody { .. } => { - todo!("handle pattern other than identifier (which repl doesn't support).\ - \nTip: this error can be triggered when trying to define a variable with a character that is not allowed, \ - like starting with an uppercase character or using underdash (_).") - } - ValueDef::Dbg { .. } => { - todo!("handle receiving a `dbg` - what should the repl do for that?") - } - ValueDef::Expect { .. } => { - todo!("handle receiving an `expect` - what should the repl do for that?") - } - ValueDef::ExpectFx { .. } => { - todo!("handle receiving an `expect-fx` - what should the repl do for that?") - } - ValueDef::ModuleImport(import) => match import.name.value.package { - Some(_) => { - todo!("handle importing a module from a package") - } - None => { - let mut filename = PathBuf::new(); + ParseOutcome::DefsAndExpr(_defs, Some(_expr)) => { + // For now if we have a expr, we bundle everything together into a single + // Defs expr, matching the older behavior of the parser. If we instead + // use the branch below, it would trigger a bug further in the compiler. - for part in import.name.value.name.parts() { - filename.push(part); - } - - filename.set_extension("roc"); + pending_past_def = None; + line + } + ParseOutcome::DefsAndExpr(defs, None) => { + let mut last_src = None; + + for def in defs.loc_defs() { + match def { + Ok(td) => { + match td.value { + TypeDef::Alias { + header: + TypeHeader { + name: Loc { value: ident, .. }, + .. + }, + .. + } + | TypeDef::Opaque { + header: + TypeHeader { + name: Loc { value: ident, .. }, + .. + }, + .. + } + | TypeDef::Ability { + header: + TypeHeader { + name: Loc { value: ident, .. }, + .. + }, + .. + } => { + // Record the type for future use. + self.add_past_def( + ident.trim_end().to_string(), + line[td.byte_range()].to_string(), + ); - // Check we can read the file before we add it to past defs. - // If we didn't do this, the bad import would remain in past_defs - // and we'd report it on every subsequent evaluation. - if let Err(err) = fs::metadata(&filename) { - return ReplAction::FileProblem { - filename, - error: err.kind(), - }; + // Return early without running eval, since none of these + // can be evaluated as expressions. + return ReplAction::Nothing; + } } - - self.past_defs.push(PastDef::Import(line.to_string())); - - return ReplAction::Nothing; } - }, - ValueDef::IngestedFileImport(file) => { - if let StrLiteral::PlainLine(path) = file.path.value { - let filename = PathBuf::from(path); - if let Err(err) = fs::metadata(&filename) { - return ReplAction::FileProblem { - filename, - error: err.kind(), - }; + Err(vd) => { + match vd.value { + ValueDef::Annotation( + Loc { + // TODO is this right for suffixed + value: Pattern::Identifier { ident }, + .. + }, + _, + ) => { + // Record the standalone type annotation for future use. + self.add_past_def( + ident.trim_end().to_string(), + line[vd.byte_range()].to_string(), + ); + + // Return early without running eval, since standalone annotations + // cannot be evaluated as expressions. + return ReplAction::Nothing; + } + ValueDef::Body( + Loc { + // TODO is this right for suffixed + value: Pattern::Identifier { ident }, + .. + }, + _, + ) + | ValueDef::AnnotatedBody { + body_pattern: + Loc { + // TODO is this right for suffixed + value: Pattern::Identifier { ident }, + .. + }, + .. + } => { + pending_past_def = Some(( + ident.to_string(), + line[vd.byte_range()].to_string(), + )); + + // Recreate the body of the def and then evaluate it as a lookup. + // We do this so that any errors will get reported as part of this expr; + // if we just did a lookup on the past def, then errors wouldn't get + // reported because we filter out errors whose regions are in past defs. + let mut buf = + bumpalo::collections::string::String::with_capacity_in( + ident.len() + line.len() + 1, + arena, + ); + + buf.push_str(line); + buf.push('\n'); + buf.push_str(ident); + + last_src = Some(buf.into_bump_str()); + } + ValueDef::Annotation(_, _) + | ValueDef::Body(_, _) + | ValueDef::AnnotatedBody { .. } => { + todo!("handle pattern other than identifier (which repl doesn't support).\ + \nTip: this error can be triggered when trying to define a variable with a character that is not allowed, \ + like starting with an uppercase character or using underdash (_).") + } + ValueDef::Dbg { .. } => { + todo!("handle receiving a `dbg` - what should the repl do for that?") + } + ValueDef::Expect { .. } => { + todo!("handle receiving an `expect` - what should the repl do for that?") + } + ValueDef::ExpectFx { .. } => { + todo!("handle receiving an `expect-fx` - what should the repl do for that?") + } + ValueDef::ModuleImport(import) => match import.name.value.package { + Some(_) => { + todo!("handle importing a module from a package") + } + None => { + let mut filename = PathBuf::new(); + + for part in import.name.value.name.parts() { + filename.push(part); + } + + filename.set_extension("roc"); + + // Check we can read the file before we add it to past defs. + // If we didn't do this, the bad import would remain in past_defs + // and we'd report it on every subsequent evaluation. + if let Err(err) = fs::metadata(&filename) { + return ReplAction::FileProblem { + filename, + error: err.kind(), + }; + } + + self.past_defs.push(PastDef::Import( + line[vd.byte_range()].to_string(), + )); + + return ReplAction::Nothing; + } + }, + ValueDef::IngestedFileImport(file) => { + if let StrLiteral::PlainLine(path) = file.path.value { + let filename = PathBuf::from(path); + if let Err(err) = fs::metadata(&filename) { + return ReplAction::FileProblem { + filename, + error: err.kind(), + }; + } + } + + self.past_defs + .push(PastDef::Import(line[vd.byte_range()].to_string())); + + return ReplAction::Nothing; + } + ValueDef::Stmt(_) => todo!(), } } - - self.past_defs.push(PastDef::Import(line.to_string())); - - return ReplAction::Nothing; } - ValueDef::Stmt(_) => todo!(), } - } - ParseOutcome::TypeDef(TypeDef::Alias { - header: - TypeHeader { - name: Loc { value: ident, .. }, - .. - }, - .. - }) - | ParseOutcome::TypeDef(TypeDef::Opaque { - header: - TypeHeader { - name: Loc { value: ident, .. }, - .. - }, - .. - }) - | ParseOutcome::TypeDef(TypeDef::Ability { - header: - TypeHeader { - name: Loc { value: ident, .. }, - .. - }, - .. - }) => { - // Record the type for future use. - self.add_past_def(ident.trim_end().to_string(), line.to_string()); - - // Return early without running eval, since none of these - // can be evaluated as expressions. - return ReplAction::Nothing; + + if let Some(src) = last_src { + src + } else { + return ReplAction::Nothing; + } } }; @@ -251,9 +288,7 @@ impl ReplState { #[derive(Debug, PartialEq)] pub enum ParseOutcome<'a> { - ValueDef(ValueDef<'a>), - TypeDef(TypeDef<'a>), - Expr(Expr<'a>), + DefsAndExpr(Defs<'a>, Option>>), Incomplete, SyntaxErr, Empty, @@ -261,6 +296,18 @@ pub enum ParseOutcome<'a> { Exit, } +/// Special case some syntax errors to allow for multi-line inputs +fn parse_outcome_for_error(e: EExpr<'_>) -> ParseOutcome<'_> { + match e { + EExpr::Closure(EClosure::Body(_, _), _) + | EExpr::When(EWhen::Pattern(EPattern::Start(_), _), _) + | EExpr::Record(_, _) + | EExpr::Start(_) + | EExpr::IndentStart(_) => ParseOutcome::Incomplete, + _ => ParseOutcome::SyntaxErr, + } +} + pub fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> { match line.trim().to_lowercase().as_str() { "" => ParseOutcome::Empty, @@ -273,161 +320,15 @@ pub fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> { _ => { let src_bytes = line.as_bytes(); - match roc_parse::expr::loc_expr(true).parse(arena, State::new(src_bytes), 0) { - Ok((_, loc_expr, _)) => ParseOutcome::Expr(loc_expr.value), - Err((roc_parse::parser::Progress::MadeProgress, EExpr::Start(_))) => { - ParseOutcome::Empty - } - // Special case some syntax errors to allow for multi-line inputs - Err((_, EExpr::Closure(EClosure::Body(_, _), _))) - | Err((_, EExpr::When(EWhen::Pattern(EPattern::Start(_), _), _))) - | Err((_, EExpr::Record(_, _))) - | Err((_, EExpr::Start(_))) - | Err((_, EExpr::IndentStart(_))) => ParseOutcome::Incomplete, - Err((_, EExpr::DefMissingFinalExpr(_))) - | Err((_, EExpr::DefMissingFinalExpr2(_, _))) => { - // This indicates that we had an attempted def; re-parse it as a single-line def. - match parse_single_def( - ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }, - 0, - arena, - State::new(src_bytes), - ) { - Ok(( - _, - Some(SingleDef { - type_or_value: Either::First(TypeDef::Alias { header, ann }), - .. - }), - state, - )) => { - // This *could* be an AnnotatedBody, e.g. in a case like this: - // - // UserId x : [UserId Int] - // UserId x = UserId 42 - // - // We optimistically parsed the first line as an alias; we might now - // turn it into an annotation. - match parse_single_def( - ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }, - 0, - arena, - state, - ) { - Ok(( - _, - Some(SingleDef { - type_or_value: - Either::Second(ValueDef::Body(loc_pattern, loc_def_expr)), - region, - spaces_before, - spaces_after: _, - }), - _, - )) if spaces_before.len() <= 1 => { - // This was, in fact, an AnnotatedBody! Build and return it. - let (value_def, _) = join_alias_to_body!( - arena, - loc_pattern, - loc_def_expr, - header, - &ann, - spaces_before, - region - ); - - ParseOutcome::ValueDef(value_def) - } - _ => { - // This was not an AnnotatedBody, so return the alias. - ParseOutcome::TypeDef(TypeDef::Alias { header, ann }) - } - } - } - Ok(( - _, - Some(SingleDef { - type_or_value: - Either::Second(ValueDef::Annotation(ann_pattern, ann_type)), - .. - }), - state, - )) => { - // This *could* be an AnnotatedBody, if the next line is a body. - match parse_single_def( - ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }, - 0, - arena, - state, - ) { - Ok(( - _, - Some(SingleDef { - type_or_value: - Either::Second(ValueDef::Body(loc_pattern, loc_def_expr)), - region, - spaces_before, - spaces_after: _, - }), - _, - )) if spaces_before.len() <= 1 => { - // Inlining this borrow makes clippy unhappy for some reason. - let ann_pattern = &ann_pattern; - - // This was, in fact, an AnnotatedBody! Build and return it. - let (value_def, _) = join_ann_to_body!( - arena, - loc_pattern, - loc_def_expr, - ann_pattern, - &ann_type, - spaces_before, - region - ); - - ParseOutcome::ValueDef(value_def) - } - _ => { - // This was not an AnnotatedBody, so return the standalone annotation. - ParseOutcome::ValueDef(ValueDef::Annotation( - ann_pattern, - ann_type, - )) - } - } - } - Ok(( - _, - Some(SingleDef { - type_or_value: Either::First(type_def), - .. - }), - _, - )) => ParseOutcome::TypeDef(type_def), - Ok(( - _, - Some(SingleDef { - type_or_value: Either::Second(value_def), - .. - }), - _, - )) => ParseOutcome::ValueDef(value_def), - Ok((_, None, _)) => { - todo!("TODO determine appropriate ParseOutcome for Ok(None)") - } - Err(_) => ParseOutcome::SyntaxErr, + match parse_repl_defs_and_optional_expr(arena, State::new(src_bytes)) { + Err((_, e)) => parse_outcome_for_error(e), + Ok((_, (defs, opt_last_expr), _state)) => { + if defs.is_empty() && opt_last_expr.is_none() { + ParseOutcome::Empty + } else { + ParseOutcome::DefsAndExpr(defs, opt_last_expr) } } - Err(_) => ParseOutcome::SyntaxErr, } } } diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index 111c523ccc1..e0a53ea3b04 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -695,6 +695,29 @@ fn to_expr_report<'a>( severity, } } + EExpr::StmtAfterExpr(pos) => { + let surroundings = Region::new(start, *pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); + + let doc = alloc.stack([ + alloc + .reflow(r"I just finished parsing an expression with a series of definitions,"), + alloc.reflow( + r"and this line is indented as if it's intended to be part of that expression:", + ), + alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), + alloc.concat([alloc.reflow( + "However, I already saw the final expression in that series of definitions.", + )]), + ]); + + Report { + filename, + doc, + title: "STATEMENT AFTER EXPRESSION".to_string(), + severity, + } + } _ => todo!("unhandled parse error: {:?}", parse_problem), } } From 7fe052322c8864a9a339514bba960b3bb8abe9ce Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 16:55:13 -0400 Subject: [PATCH 043/203] update test, fix logic --- crates/compiler/fmt/src/def.rs | 9 +++------ crates/compiler/test_syntax/tests/test_fmt.rs | 15 ++++----------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 40fd59faae1..2db593b5e6b 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -590,9 +590,7 @@ pub fn fmt_annotated_body_comment<'a>( let mut comment_iter = comment.iter(); if let Some(comment_first) = comment_iter.next() { match comment_first { - roc_parse::ast::CommentOrNewline::Newline => { - buf.newline(); - } + roc_parse::ast::CommentOrNewline::Newline => (), roc_parse::ast::CommentOrNewline::DocComment(comment_str) => { buf.push_str(" # #"); buf.spaces(1); @@ -606,10 +604,9 @@ pub fn fmt_annotated_body_comment<'a>( } for comment_or_newline in comment_iter { + buf.newline(); match comment_or_newline { - roc_parse::ast::CommentOrNewline::Newline => { - buf.newline(); - } + roc_parse::ast::CommentOrNewline::Newline => (), roc_parse::ast::CommentOrNewline::DocComment(comment_str) => { buf.indent(indent); buf.push_str("# #"); diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 5fae1dc6c7a..7c76887ad46 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6231,24 +6231,17 @@ mod test_fmt { } #[test] - fn issue_6896() { - expr_formats_to( - indoc!( - r" - x : i32 - # comment - x = 1 - x - " - ), + fn preserve_annotated_body_comments() { + expr_formats_same( indoc!( r" x : i32 # comment + # comment 2 x = 1 x " - ), + ) ); } From 4e4d9b0d4c670ed1bdd8ccd2f736d1de01f4fc1b Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 16:59:34 -0400 Subject: [PATCH 044/203] add tests --- crates/compiler/test_syntax/tests/test_fmt.rs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 7c76887ad46..a2a92f61b41 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6230,6 +6230,32 @@ mod test_fmt { ); } + #[test] + fn preserve_annotated_body() { + expr_formats_same( + indoc!( + r" + x : i32 + x = 1 + x + " + ) + ); + } + + #[test] + fn preserve_annotated_body_comment() { + expr_formats_same( + indoc!( + r" + x : i32 # comment + x = 1 + x + " + ) + ); + } + #[test] fn preserve_annotated_body_comments() { expr_formats_same( @@ -6245,6 +6271,52 @@ mod test_fmt { ); } + #[test] + fn preserve_annotated_body_comments_with_newlines() { + expr_formats_same( + indoc!( + r" + x : i32 + + # comment + + # comment 2 + + x = 1 + x + " + ) + ); + } + + #[test] + fn preserve_annotated_body_blank_comment() { + expr_formats_same( + indoc!( + r" + x : i32 + # + x = 1 + x + " + ) + ); + } + + #[test] + fn preserve_annotated_body_with_newlines() { + expr_formats_same( + indoc!( + r" + x : i32 + + x = 1 + x + " + ) + ); + } + // this is a parse error atm // #[test] // fn multiline_apply() { From 62dbe9fb53269646cec1f316ddefd53bddeb8a8a Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 17:04:28 -0400 Subject: [PATCH 045/203] update snapshots --- .../snapshots/test_suffixed__suffixed_tests__basic.snap | 2 +- .../test_suffixed__suffixed_tests__closure_simple.snap | 2 +- ...uffixed__suffixed_tests__closure_with_annotations.snap | 6 ++++-- .../test_suffixed__suffixed_tests__closure_with_defs.snap | 8 +++++--- ...st_suffixed__suffixed_tests__defs_suffixed_middle.snap | 2 +- .../test_suffixed__suffixed_tests__if_complex.snap | 8 +++++--- ..._suffixed__suffixed_tests__last_suffixed_multiple.snap | 4 ++-- .../test_suffixed__suffixed_tests__multi_defs_stmts.snap | 2 +- .../test_suffixed__suffixed_tests__multiple_suffix.snap | 2 +- .../test_suffixed__suffixed_tests__simple_pizza.snap | 2 +- .../test_suffixed__suffixed_tests__trailing_binops.snap | 2 +- .../test_suffixed__suffixed_tests__type_annotation.snap | 2 +- .../test_suffixed__suffixed_tests__when_branches.snap | 2 +- .../tests/snapshots/pass/ann_closed_union.expr.result-ast | 4 +++- .../tests/snapshots/pass/ann_open_union.expr.result-ast | 4 +++- .../pass/annotated_record_destructure.expr.result-ast | 4 +++- .../pass/annotated_tag_destructure.expr.result-ast | 4 +++- .../pass/annotated_tuple_destructure.expr.result-ast | 4 +++- .../snapshots/pass/fn_with_record_arg.expr.result-ast | 4 +++- .../pass/function_with_tuple_ext_type.expr.result-ast | 4 +++- .../pass/function_with_tuple_type.expr.result-ast | 4 +++- .../pass/nested_def_annotation.moduledefs.result-ast | 4 +++- .../tests/snapshots/pass/tuple_type.expr.result-ast | 4 +++- .../tests/snapshots/pass/tuple_type_ext.expr.result-ast | 4 +++- .../snapshots/pass/type_signature_def.expr.result-ast | 4 +++- .../pass/type_signature_function_def.expr.result-ast | 4 +++- .../tests/snapshots/pass/where_ident.expr.result-ast | 4 +++- 27 files changed, 67 insertions(+), 33 deletions(-) diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index c78f101cc81..cd0529a633b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -60,7 +60,7 @@ Defs { @11-15 Inferred, ], ), - comment: None, + comment: [], body_pattern: @11-15 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index 4ed25bf707f..7192ff4bf58 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -87,7 +87,7 @@ Defs { @31-42 Inferred, ], ), - comment: None, + comment: [], body_pattern: @31-42 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index c88b75ab111..4769022e653 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -60,7 +60,9 @@ Defs { ], ), ), - comment: None, + comment: [ + Newline, + ], body_pattern: @35-36 Identifier { ident: "x", }, @@ -109,7 +111,7 @@ Defs { @60-69 Inferred, ], ), - comment: None, + comment: [], body_pattern: @78-79 Identifier { ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index 490fddac5a7..a22563390f1 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -76,7 +76,9 @@ Defs { ], ), ), - comment: None, + comment: [ + Newline, + ], body_pattern: @50-53 Identifier { ident: "foo", }, @@ -130,7 +132,7 @@ Defs { @76-83 Inferred, ], ), - comment: None, + comment: [], body_pattern: @76-83 Identifier { ident: "#!1_stmt", }, @@ -199,7 +201,7 @@ Defs { @92-99 Inferred, ], ), - comment: None, + comment: [], body_pattern: @92-99 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index a07119b6c8a..02fa2c025a8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -96,7 +96,7 @@ Defs { @25-39 Inferred, ], ), - comment: None, + comment: [], body_pattern: @25-39 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index 9e62fed3526..49e84ae5d01 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -112,7 +112,9 @@ Defs { ), ], ), - comment: None, + comment: [ + Newline, + ], body_pattern: @95-98 Identifier { ident: "msg", }, @@ -190,7 +192,7 @@ Defs { @140-152 Inferred, ], ), - comment: None, + comment: [], body_pattern: @140-152 Identifier { ident: "#!0_stmt", }, @@ -312,7 +314,7 @@ Defs { @227-239 Inferred, ], ), - comment: None, + comment: [], body_pattern: @227-239 Identifier { ident: "#!2_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index 8a090182841..d04a8f158d6 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -60,7 +60,7 @@ Defs { @11-15 Inferred, ], ), - comment: None, + comment: [], body_pattern: @11-15 Identifier { ident: "#!2_stmt", }, @@ -120,7 +120,7 @@ Defs { @20-24 Inferred, ], ), - comment: None, + comment: [], body_pattern: @20-24 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 3f9caf99ca0..26f13716096 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -60,7 +60,7 @@ Defs { @11-23 Inferred, ], ), - comment: None, + comment: [], body_pattern: @11-23 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index 4e2c7cb925d..4e2be3a2b5b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -67,7 +67,7 @@ Defs { @11-16 Inferred, ], ), - comment: None, + comment: [], body_pattern: @11-16 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 5bf0ebd4e0c..3b18cbad90d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -60,7 +60,7 @@ Defs { @26-56 Inferred, ], ), - comment: None, + comment: [], body_pattern: @26-56 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index 52dbd271583..a8c9e23ed52 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -69,7 +69,7 @@ Defs { @19-30 Inferred, ], ), - comment: None, + comment: [], body_pattern: @19-30 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index e53888deea0..0165aa816c9 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -67,7 +67,7 @@ Defs { @18-19 Inferred, ], ), - comment: None, + comment: [], body_pattern: @24-25 Identifier { ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index c8ef89774fa..4b21aa7701c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -88,7 +88,7 @@ Defs { @54-65 Inferred, ], ), - comment: None, + comment: [], body_pattern: @54-65 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast index 9799893661d..63f4039b00e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast @@ -38,7 +38,9 @@ Defs( }, ], }, - comment: None, + comment: [ + Newline, + ], body_pattern: @28-31 Identifier { ident: "foo", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast index 149c8e6e4ef..d15d991386b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast @@ -40,7 +40,9 @@ Defs( }, ], }, - comment: None, + comment: [ + Newline, + ], body_pattern: @29-32 Identifier { ident: "foo", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast index 52cac4a3cac..1fe68ecf194 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast @@ -31,7 +31,9 @@ Defs( "Foo", [], ), - comment: None, + comment: [ + Newline, + ], body_pattern: @15-23 RecordDestructure( [ @17-18 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast index 6b6a39c5b03..e0fa74c1b84 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast @@ -41,7 +41,9 @@ Defs( }, ], }, - comment: None, + comment: [ + Newline, + ], body_pattern: @26-34 Apply( @26-32 Tag( "UserId", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast index 49768c21563..536a28f9d59 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast @@ -31,7 +31,9 @@ Defs( "Foo", [], ), - comment: None, + comment: [ + Newline, + ], body_pattern: @15-23 Tuple( [ @17-18 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast index bba8dbce128..fe3a44b0003 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast @@ -52,7 +52,9 @@ Defs( [], ), ), - comment: None, + comment: [ + Newline, + ], body_pattern: @45-50 Identifier { ident: "table", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index 3f0d8f387e5..a7f483ed108 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -51,7 +51,9 @@ Defs( ), }, ), - comment: None, + comment: [ + Newline, + ], body_pattern: @21-22 Identifier { ident: "f", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index c150ac78960..7ae2052e04c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -43,7 +43,9 @@ Defs( ext: None, }, ), - comment: None, + comment: [ + Newline, + ], body_pattern: @22-23 Identifier { ident: "f", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index fe6b7bbee43..cacbe0d878a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -55,7 +55,9 @@ Defs { [], ), ), - comment: None, + comment: [ + Newline, + ], body_pattern: @43-55 Identifier { ident: "wrappedNotEq", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast index b4c87cc1101..e4ffc41f7cf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast @@ -53,7 +53,9 @@ Defs( ext: None, }, ), - comment: None, + comment: [ + Newline, + ], body_pattern: @28-29 Identifier { ident: "f", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast index cf89afd755f..0c1fdfd0354 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast @@ -61,7 +61,9 @@ Defs( ), }, ), - comment: None, + comment: [ + Newline, + ], body_pattern: @30-31 Identifier { ident: "f", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast index 8e3cc68fd24..5aa0513780c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast @@ -24,7 +24,9 @@ Defs( "Int", [], ), - comment: None, + comment: [ + Newline, + ], body_pattern: @10-13 Identifier { ident: "foo", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast index 4b923b7739f..69d9ef27edd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast @@ -38,7 +38,9 @@ Defs( [], ), ), - comment: None, + comment: [ + Newline, + ], body_pattern: @25-28 Identifier { ident: "foo", }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast index b362ed0e8d7..0fc251565c4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast @@ -33,7 +33,9 @@ Defs( ], ext: None, }, - comment: None, + comment: [ + Newline, + ], body_pattern: @21-26 Identifier { ident: "where", }, From 2738cd717d36ecf943361b5c04dc321800e5813c Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 27 Jul 2024 17:37:28 -0400 Subject: [PATCH 046/203] cargo fmt --- crates/compiler/test_syntax/tests/test_fmt.rs | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index a2a92f61b41..64e8d8d5928 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6232,50 +6232,43 @@ mod test_fmt { #[test] fn preserve_annotated_body() { - expr_formats_same( - indoc!( - r" + expr_formats_same(indoc!( + r" x : i32 x = 1 x " - ) - ); + )); } #[test] fn preserve_annotated_body_comment() { - expr_formats_same( - indoc!( - r" + expr_formats_same(indoc!( + r" x : i32 # comment x = 1 x " - ) - ); + )); } #[test] fn preserve_annotated_body_comments() { - expr_formats_same( - indoc!( - r" + expr_formats_same(indoc!( + r" x : i32 # comment # comment 2 x = 1 x " - ) - ); + )); } #[test] fn preserve_annotated_body_comments_with_newlines() { - expr_formats_same( - indoc!( - r" + expr_formats_same(indoc!( + r" x : i32 # comment @@ -6285,36 +6278,31 @@ mod test_fmt { x = 1 x " - ) - ); + )); } #[test] fn preserve_annotated_body_blank_comment() { - expr_formats_same( - indoc!( - r" + expr_formats_same(indoc!( + r" x : i32 # x = 1 x " - ) - ); + )); } #[test] fn preserve_annotated_body_with_newlines() { - expr_formats_same( - indoc!( - r" + expr_formats_same(indoc!( + r" x : i32 x = 1 x " - ) - ); + )); } // this is a parse error atm From ecb8b12167706778b4dc6f0a2a71ebb0e8cd1bba Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 27 Jul 2024 14:49:56 -0700 Subject: [PATCH 047/203] Add back working tests --- crates/compiler/test_syntax/tests/test_fmt.rs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 683084b6f40..ec4ee347f5e 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -5442,6 +5442,73 @@ mod test_fmt { )); } + #[test] + fn backpassing_parens_body() { + expr_formats_same(indoc!( + r" + Task.fromResult + ( + b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + )); + + expr_formats_to( + indoc!( + r" + Task.fromResult + (b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + ), + indoc!( + r" + Task.fromResult + ( + b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + ), + ); + + expr_formats_to( + indoc!( + r" + Task.fromResult + (b <- binaryOp ctx + if a == b then + -1 + else + 0) + " + ), + indoc!( + r" + Task.fromResult + ( + b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + ), + ); + } + #[test] fn backpassing_body_on_newline() { expr_formats_same(indoc!( From 413de7f72ed84aa8ebc10505af4c01928d0410f8 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 28 Jul 2024 11:37:40 -0400 Subject: [PATCH 048/203] s/roc/Roc in an error message Signed-off-by: Richard Feldman --- crates/compiler/load/tests/test_reporting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index c50f453235e..f76c575fca1 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -5442,7 +5442,7 @@ mod test_reporting { Looks like you are trying to define a function. - In roc, functions are always written as a lambda, like + In Roc, functions are always written as a lambda, like increment = \n -> n + 1 "### From df915b936df1b8a73e9c26909880db75bfd8e0c6 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sun, 28 Jul 2024 12:01:20 -0700 Subject: [PATCH 049/203] Feedback: add doc comments, verbiage changes, capitalize Roc, remove a resolved TODO --- crates/compiler/load/tests/test_reporting.rs | 8 +-- crates/compiler/parse/src/expr.rs | 66 ++++++++++++++++++- .../compiler/test_syntax/src/bin/minimize.rs | 12 ++++ crates/compiler/test_syntax/src/minimize.rs | 9 ++- crates/repl_test/src/tests.rs | 2 +- crates/reporting/src/error/parse.rs | 8 +-- 6 files changed, 93 insertions(+), 12 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index f76c575fca1..5c6eee967c2 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -3374,7 +3374,7 @@ mod test_reporting { f x y = x " ), - @r#" + @r###" ── ARGUMENTS BEFORE EQUALS in tmp/elm_function_syntax/Test.roc ───────────────── I am partway through parsing a definition, but I got stuck here: @@ -3385,9 +3385,9 @@ mod test_reporting { 4│ f x y = x ^^^ - Looks like you are trying to define a function. In roc, functions are + Looks like you are trying to define a function. In Roc, functions are always written as a lambda, like increment = \n -> n + 1. - "# + "### ); test_report!( @@ -5030,7 +5030,7 @@ mod test_reporting { 4│ import svg.Path a ^ - I was expecting to see the `as` keyword, like: + I was expecting to see the `as` keyword next, like: import svg.Path as SvgPath diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index eaa5a1eaccd..8b7948994d1 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -339,6 +339,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> { } } +/// Entry point for parsing an expression. fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, EExpr<'a>> { one_of![ loc(specialize_err(EExpr::If, if_expr_help(options))), @@ -350,6 +351,7 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, E .trace("expr_start") } +/// Parse a chain of expressions separated by operators. Also handles function application. fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { (move |arena, state: State<'a>, min_indent: u32| { parse_expr_operator_chain(arena, state, min_indent, options) @@ -431,6 +433,9 @@ fn parse_expr_operator_chain<'a>( } } +/// We're part way thru parsing an expression, e.g. `bar foo `. +/// We just tried parsing an argument and determined we couldn't - +/// so we're going to try parsing an operator. #[allow(clippy::too_many_arguments)] fn parse_expr_after_apply<'a>( arena: &'a Bump, @@ -1090,6 +1095,7 @@ fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option( arena: &'a Bump, @@ -1331,6 +1337,7 @@ mod ability { } } +/// Parse the series of "demands" (e.g. similar to methods in a rust trait), for an ability definition. fn finish_parsing_ability_def_help<'a>( call_min_indent: u32, name: Loc<&'a str>, @@ -1379,6 +1386,16 @@ fn finish_parsing_ability_def_help<'a>( Ok((MadeProgress, (type_def, def_region), state)) } +/// A Stmt is an intermediate representation used only during parsing. +/// It consists of a fragment of code that hasn't been fully stitched together yet. +/// For example, each of the following lines is a Stmt: +/// - `foo bar` (Expr) +/// - `foo, bar <- baz` (Backpassing) +/// - `Foo : [A, B, C]` (TypeDef) +/// - `foo = \x -> x + 1` (ValueDef) +/// +/// Note in particular that the Backpassing Stmt doesn't make any sense on its own; +/// we need to link it up with the following stmts to make a complete expression. #[derive(Debug, Clone, Copy)] pub enum Stmt<'a> { Expr(Expr<'a>), @@ -1387,6 +1404,11 @@ pub enum Stmt<'a> { ValueDef(ValueDef<'a>), } +/// Having just parsed an operator, we need to dispatch to the appropriate +/// parsing function based on the operator. +/// +/// Note, this function is very similar to `parse_expr_operator`, but it +/// handles additional cases to allow assignments / type annotations / etc. #[allow(clippy::too_many_arguments)] fn parse_stmt_operator<'a>( arena: &'a Bump, @@ -1462,6 +1484,8 @@ fn parse_stmt_operator<'a>( } } +/// We just parsed an operator. Parse the expression that follows, taking special care +/// that this might be a negated term. (`-x` is a negated term, not a binary operation) #[allow(clippy::too_many_arguments)] fn parse_expr_operator<'a>( arena: &'a Bump, @@ -1508,6 +1532,7 @@ fn parse_expr_operator<'a>( } } +/// Continue parsing terms after we just parsed a binary operator #[allow(clippy::too_many_arguments)] fn parse_after_binop<'a>( arena: &'a Bump, @@ -1581,6 +1606,7 @@ fn parse_after_binop<'a>( } } +/// Parse the rest of a backpassing statement, after the <- operator fn parse_stmt_backpassing<'a>( arena: &'a Bump, state: State<'a>, @@ -1590,8 +1616,6 @@ fn parse_stmt_backpassing<'a>( options: ExprParseOptions, spaces_after_operator: &'a [CommentOrNewline], ) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { - // called after parsing the <- operator - let expr_region = expr_state.expr.region; let call = expr_state @@ -1627,6 +1651,8 @@ fn parse_stmt_backpassing<'a>( Ok((MadeProgress, ret, state)) } +/// We just saw a `,` that we think is part of a backpassing statement. +/// Parse the rest of the statement. fn parse_stmt_multi_backpassing<'a>( mut expr_state: ExprState<'a>, arena: &'a Bump, @@ -1689,6 +1715,7 @@ fn parse_stmt_multi_backpassing<'a>( } } +/// We just saw the '=' operator of an assignment stmt. Continue parsing from there. fn parse_stmt_assignment<'a>( arena: &'a Bump, state: State<'a>, @@ -1733,6 +1760,7 @@ fn parse_stmt_assignment<'a>( Ok((MadeProgress, Stmt::ValueDef(value_def), state)) } +/// We just saw a unary negation operator, and now we need to parse the expression. #[allow(clippy::too_many_arguments)] fn parse_negated_term<'a>( arena: &'a Bump, @@ -1779,6 +1807,8 @@ fn parse_negated_term<'a>( ) } +/// Parse an expression, not allowing `if`/`when`/etc. +/// TODO: this should probably be subsumed into `parse_expr_operator_chain` #[allow(clippy::too_many_arguments)] fn parse_expr_end<'a>( arena: &'a Bump, @@ -1837,6 +1867,13 @@ fn parse_expr_end<'a>( } } +/// We're part way thru parsing an expression, e.g. `bar foo `. +/// We just tried parsing an argument and determined we couldn't - +/// so we're going to try parsing an operator. +/// +/// Note that this looks a lot like `parse_expr_after_apply`, except +/// we handle the additional case of backpassing, which is valid +/// at the statement level but not at the expression level. fn parse_stmt_after_apply<'a>( arena: &'a Bump, state: State<'a>, @@ -2679,6 +2716,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< } } +/// Parse a block of statements (parser combinator version of `parse_block`) fn block<'a, E>( options: ExprParseOptions, require_indent: bool, @@ -2701,6 +2739,11 @@ where .trace("block") } +/// Parse a block of statements. +/// For example, the then and else branches of an `if` expression are both blocks. +/// There are two cases here: +/// 1. If there is a preceding newline, then the block must be indented and is allowed to have definitions. +/// 2. If there is no preceding newline, then the block must consist of a single expression (no definitions). fn parse_block<'a, E>( options: ExprParseOptions, arena: &'a Bump, @@ -2735,6 +2778,9 @@ where ) } +/// Parse a block of statements, and process that into an Expr. +/// Assumes the caller has already parsed the optional first "space" (newline), +/// and decided whether to allow definitions. #[allow(clippy::too_many_arguments)] fn parse_block_inner<'a, E>( options: ExprParseOptions, @@ -2797,6 +2843,15 @@ where } } +/// Parse a sequence of statements, which we'll later process into an expression. +/// Statements can include: +/// - assignments +/// - type annotations +/// - expressions +/// - [multi]backpassing +/// +/// This function doesn't care about whether the order of those statements makes any sense. +/// e.g. it will happily parse two expressions in a row, or backpassing with nothing following it. fn parse_stmt_seq<'a, E: SpaceProblem + 'a>( arena: &'a Bump, mut state: State<'a>, @@ -2868,6 +2923,7 @@ fn parse_stmt_seq<'a, E: SpaceProblem + 'a>( Ok((MadeProgress, stmts, state)) } +/// Check if the current byte is a terminator for a sequence of statements fn at_terminator(state: &State<'_>) -> bool { matches!( state.bytes().first(), @@ -2875,6 +2931,8 @@ fn at_terminator(state: &State<'_>) -> bool { ) } +/// Convert a sequence of statements into a `Expr::Defs` expression +/// (which is itself a Defs struct and final expr) fn stmts_to_expr<'a>( stmts: &[SpacesBefore<'a, Loc>>], arena: &'a Bump, @@ -2940,6 +2998,9 @@ fn stmts_to_expr<'a>( } } +/// Convert a sequence of `Stmt` into a Defs and an optional final expression. +/// Future refactoring opportunity: push this logic directly into where we're +/// parsing the statements. fn stmts_to_defs<'a>( stmts: &[SpacesBefore<'a, Loc>>], mut defs: Defs<'a>, @@ -3133,6 +3194,7 @@ fn stmts_to_defs<'a>( Ok((defs, last_expr)) } +/// Given a type alias and a value definition, join them into a AnnotatedBody pub fn join_alias_to_body<'a>( arena: &'a Bump, header: TypeHeader<'a>, diff --git a/crates/compiler/test_syntax/src/bin/minimize.rs b/crates/compiler/test_syntax/src/bin/minimize.rs index 404a45b5a67..bac6e536c6e 100644 --- a/crates/compiler/test_syntax/src/bin/minimize.rs +++ b/crates/compiler/test_syntax/src/bin/minimize.rs @@ -1,3 +1,15 @@ +//! Generate a minimized version of a given input, by removing parts of it. +//! This is useful for debugging, when you have a large input that causes a failure, +//! and you want to find the smallest input that still causes the failure. +//! +//! Typical usage: +//! `cargo run --release --bin minimize -- full ` +//! +//! This tool will churn on that for a while, and eventually print out a minimized version +//! of the input that still triggers the bug. +//! +//! Note that `--release` is important, as this tool is very slow in debug mode. + use test_syntax::{minimize::print_minimizations, test_helpers::InputKind}; fn main() { diff --git a/crates/compiler/test_syntax/src/minimize.rs b/crates/compiler/test_syntax/src/minimize.rs index d8fd4647d41..946b31147e0 100644 --- a/crates/compiler/test_syntax/src/minimize.rs +++ b/crates/compiler/test_syntax/src/minimize.rs @@ -1,3 +1,10 @@ +//! Generate a minimized version of a given input, by removing parts of it. +//! This is useful for debugging, when you have a large input that causes a failure, +//! and you want to find the smallest input that still causes the failure. +//! +//! Most users will want to use the binary instead of this module directly. +//! e.g. `cargo run --release --bin minimize -- full ` + use crate::test_helpers::{Input, InputKind}; use bumpalo::Bump; use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces}; @@ -79,7 +86,7 @@ fn round_trip_once(input: Input<'_>) -> Option { "Initial parse failed: {:?}", e.remove_spaces(&arena) )) - } // todo: strip pos info, use the error + } }; if actual.is_malformed() { diff --git a/crates/repl_test/src/tests.rs b/crates/repl_test/src/tests.rs index 5aae4e2e1dc..7af99812c76 100644 --- a/crates/repl_test/src/tests.rs +++ b/crates/repl_test/src/tests.rs @@ -1133,7 +1133,7 @@ fn parse_problem() { 4│ add m n = m + n ^^^ - Looks like you are trying to define a function. In roc, functions are + Looks like you are trying to define a function. In Roc, functions are always written as a lambda, like increment = \n -> n + 1. "# ), diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index e0a53ea3b04..c37482cfb67 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -203,7 +203,7 @@ fn to_expr_report<'a>( alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), alloc.concat([ alloc.reflow("Looks like you are trying to define a function. "), - alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.reflow("In Roc, functions are always written as a lambda, like "), alloc.parser_suggestion("increment = \\n -> n + 1"), alloc.reflow("."), ]), @@ -257,7 +257,7 @@ fn to_expr_report<'a>( Context::InDef(_pos) => { vec![alloc.stack([ alloc.reflow("Looks like you are trying to define a function. "), - alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.reflow("In Roc, functions are always written as a lambda, like "), alloc .parser_suggestion("increment = \\n -> n + 1") .indent(4), @@ -509,7 +509,7 @@ fn to_expr_report<'a>( alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), alloc.concat([ alloc.reflow("Looks like you are trying to define a function. "), - alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.reflow("In Roc, functions are always written as a lambda, like "), alloc.parser_suggestion("increment = \\n -> n + 1"), alloc.reflow("."), ]), @@ -1541,7 +1541,7 @@ fn to_import_report<'a>( alloc.concat([ alloc.reflow("I was expecting to see the "), alloc.keyword("as"), - alloc.reflow(" keyword, like:"), + alloc.reflow(" keyword next, like:"), ]), alloc .parser_suggestion("import svg.Path as SvgPath") From 52af8c588e2ea99b92d9bf579d3b74ce9cd909c5 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 17:47:53 -0700 Subject: [PATCH 050/203] start adding the basis for quadsort for blitsort --- crates/compiler/builtins/bitcode/src/list.zig | 66 +--- crates/compiler/builtins/bitcode/src/sort.zig | 293 ++++++++++++++++++ 2 files changed, 300 insertions(+), 59 deletions(-) create mode 100644 crates/compiler/builtins/bitcode/src/sort.zig diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index ce82aa4c4ee..f61bb3ff19e 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -1,6 +1,7 @@ const std = @import("std"); const utils = @import("utils.zig"); const str = @import("str.zig"); +const sort = @import("sort.zig"); const UpdateMode = utils.UpdateMode; const mem = std.mem; const math = std.math; @@ -690,60 +691,10 @@ pub fn listDropAt( } } -fn partition( - source_ptr: [*]u8, - transform: Opaque, - wrapper: CompareFn, - element_width: usize, - low: isize, - high: isize, - copy: CopyFn, -) isize { - const pivot = source_ptr + (@as(usize, @intCast(high)) * element_width); - var i = (low - 1); // Index of smaller element and indicates the right position of pivot found so far - var j = low; - - while (j <= high - 1) : (j += 1) { - const current_elem = source_ptr + (@as(usize, @intCast(j)) * element_width); - - const ordering = wrapper(transform, current_elem, pivot); - const order = @as(utils.Ordering, @enumFromInt(ordering)); - - switch (order) { - utils.Ordering.LT => { - // the current element is smaller than the pivot; swap it - i += 1; - swapElements(source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j)), copy); - }, - utils.Ordering.EQ, utils.Ordering.GT => {}, - } - } - swapElements(source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high)), copy); - return (i + 1); -} - -fn quicksort( - source_ptr: [*]u8, - transform: Opaque, - wrapper: CompareFn, - element_width: usize, - low: isize, - high: isize, - copy: CopyFn, -) void { - if (low < high) { - // partition index - const pi = partition(source_ptr, transform, wrapper, element_width, low, high, copy); - - _ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1, copy); // before pi - _ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high, copy); // after pi - } -} - pub fn listSortWith( input: RocList, - caller: CompareFn, - data: Opaque, + cmp: CompareFn, + cmp_data: Opaque, inc_n_data: IncN, data_is_owned: bool, alignment: u32, @@ -753,16 +704,13 @@ pub fn listSortWith( dec: Dec, copy: CopyFn, ) callconv(.C) RocList { - var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec); - - if (data_is_owned) { - inc_n_data(data, list.len()); + if (input.len() < 2) { + return input; } + var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec); if (list.bytes) |source_ptr| { - const low = 0; - const high: isize = @as(isize, @intCast(list.len())) - 1; - quicksort(source_ptr, data, caller, element_width, low, high, copy); + sort.quadsort(source_ptr, list.len(), cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy); } return list; diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig new file mode 100644 index 00000000000..10fcc122a3a --- /dev/null +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -0,0 +1,293 @@ +const std = @import("std"); +const testing = std.testing; + +const utils = @import("utils.zig"); +const roc_panic = @import("panic.zig").panic_help; + +const Opaque = ?[*]u8; +const CompareFn = *const fn (Opaque, Opaque, Opaque) callconv(.C) u8; +const CopyFn = *const fn (Opaque, Opaque) callconv(.C) void; +const IncN = *const fn (?[*]u8, usize) callconv(.C) void; + +/// Any size larger than the max element buffer will be sorted indirectly via pointers. +const MAX_ELEMENT_BUFFER_SIZE: usize = 64; + +pub fn quadsort( + source_ptr: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + data_is_owned: bool, + inc_n_data: IncN, + element_width: usize, + copy: CopyFn, +) void { + if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { + quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy); + } else { + roc_panic("todo: fallback to an indirect pointer sort", 0); + } +} + +fn quadsort_direct( + source_ptr: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + data_is_owned: bool, + inc_n_data: IncN, + element_width: usize, + copy: CopyFn, +) void { + _ = inc_n_data; + _ = data_is_owned; + _ = cmp_data; + _ = len; + _ = copy; + _ = element_width; + _ = cmp; + _ = source_ptr; + roc_panic("todo: quadsort", 0); +} + +/// Merge two neighboring sorted 4 element arrays into swap. +inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var left = ptr; + var right = ptr + (4 * element_width); + var swap_ptr = swap; + head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) != utils.Ordering.GT; + var to_copy = if (lte) left else right; + copy(swap_ptr, to_copy); + + left = ptr + (3 * element_width); + right = ptr + (7 * element_width); + swap_ptr = swap + (7 * element_width); + tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) == utils.Ordering.GT; + to_copy = if (gt) left else right; + copy(swap_ptr, to_copy); +} + +/// Merge two neighboring sorted 2 element arrays into swap. +inline fn parity_merge_two(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var left = ptr; + var right = ptr + (2 * element_width); + var swap_ptr = swap; + head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) != utils.Ordering.GT; + var to_copy = if (lte) left else right; + copy(swap_ptr, to_copy); + + left = ptr + element_width; + right = ptr + (3 * element_width); + swap_ptr = swap + (3 * element_width); + tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) == utils.Ordering.GT; + to_copy = if (gt) left else right; + copy(swap_ptr, to_copy); +} + +/// Moves the smaller element from left and rigth to dest. +/// Will increment both dest and the smaller element ptr to their next index. +/// Inlining will remove the extra level of pointer indirection here. +/// It is just used to allow mutating the input pointers. +inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + // Note there is a much simpler version here: + // *ptd++ = cmp(ptl, ptr) <= 0 ? *ptl++ : *ptr++; + // That said, it is only used with gcc, so I assume it has optimization issues with llvm. + // Thus using the longer form. + const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left.*, right.*))) != utils.Ordering.GT; + // TODO: double check this is branchless. + const x = if (lte) element_width else 0; + const not_x = if (lte) 0 else element_width; + copy(dest.*, left.*); + left.* += x; + copy((dest.* + x), right.*); + right.* += not_x; + dest.* += element_width; +} + +/// Moves the smaller element from left and rigth to dest. +/// Will decrement both dest and the smaller element ptr to their previous index. +/// Inlining will remove the extra level of pointer indirection here. +/// It is just used to allow mutating the input pointers. +inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + // Note there is a much simpler version here: + // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; + // That said, it is only used with gcc, so I assume it has optimization issues with llvm. + // Thus using the longer form. + const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left.*, right.*))) != utils.Ordering.GT; + // TODO: double check this is branchless. + const y = if (lte) element_width else 0; + const not_y = if (lte) 0 else element_width; + copy(dest.*, left.*); + left.* -= not_y; + dest.* -= element_width; + copy((dest.* + y), right.*); + right.* -= y; +} + +/// Swaps the element at ptr with the element after it if the element is greater than the next. +inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, ptr, ptr + element_width))) == utils.Ordering.GT; + // TODO: double check this is branchless. I would expect llvm to optimize this to be branchless. + // But based on reading some comments in quadsort, llvm seems to prefer branches very often. + const x = if (gt) element_width else 0; + const y = if (gt) 0 else element_width; + + copy(swap, ptr + y); + copy(ptr, ptr + x); + copy(ptr + element_width, swap); +} + +test "parity_merge_four" { + var arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; + var swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + + arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; + swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + + parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + + arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; + swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + + parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); +} + +test "parity_merge_two" { + var arr = [4]i64{ 1, 2, 3, 4 }; + var swap = [4]i64{ 0, 0, 0, 0 }; + + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 1, 3, 2, 4 }; + swap = [4]i64{ 0, 0, 0, 0 }; + + parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 3, 4, 1, 2 }; + swap = [4]i64{ 0, 0, 0, 0 }; + + parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 2, 4, 1, 3 }; + swap = [4]i64{ 0, 0, 0, 0 }; + + parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 1, 4, 2, 3 }; + swap = [4]i64{ 0, 0, 0, 0 }; + + parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); +} + +test "head_merge" { + var dest = [6]i64{ 0, 0, 0, 0, 0, 0 }; + var left = [4]i64{ 1, 7, 10, 22 }; + var right = [4]i64{ 2, 2, 8, 22 }; + var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); + var left_ptr = @as([*]u8, @ptrCast(&left[0])); + var right_ptr = @as([*]u8, @ptrCast(&right[0])); + + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); +} + +test "tail_merge" { + var dest = [6]i64{ 0, 0, 0, 0, 0, 0 }; + var left = [4]i64{ -22, 1, 7, 10 }; + var right = [4]i64{ -22, 2, 2, 8 }; + var dest_ptr = @as([*]u8, @ptrCast(&dest[dest.len - 1])); + var left_ptr = @as([*]u8, @ptrCast(&left[left.len - 1])); + var right_ptr = @as([*]u8, @ptrCast(&right[right.len - 1])); + + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); +} + +test "swap" { + var arr = [2]i64{ 10, 20 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: i64 = 0; + var swap_ptr = @as([*]u8, @ptrCast(&swap)); + + swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr[0], 10); + try testing.expectEqual(arr[1], 20); + + arr[0] = 77; + arr[1] = -12; + + swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr[0], -12); + try testing.expectEqual(arr[1], 77); + + arr[0] = -22; + arr[1] = -22; + + swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr[0], -22); + try testing.expectEqual(arr[1], -22); +} + +fn test_i64_compare(_: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { + const a = @as(*i64, @alignCast(@ptrCast(a_ptr))).*; + const b = @as(*i64, @alignCast(@ptrCast(b_ptr))).*; + + const gt = @as(u8, @intFromBool(a > b)); + const lt = @as(u8, @intFromBool(a < b)); + + // Eq = 0 + // GT = 1 + // LT = 2 + return lt + lt + gt; +} + +fn test_i64_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { + @as(*i64, @alignCast(@ptrCast(dst_ptr))).* = @as(*i64, @alignCast(@ptrCast(src_ptr))).*; +} From 8b58161c73ef42b348e94b2ce93cf64102c7cb53 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 17:50:44 -0700 Subject: [PATCH 051/203] correct comment on branchless version --- crates/compiler/builtins/bitcode/src/sort.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 10fcc122a3a..2e815e4a1f0 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -99,7 +99,7 @@ inline fn parity_merge_two(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Compa inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { // Note there is a much simpler version here: // *ptd++ = cmp(ptl, ptr) <= 0 ? *ptl++ : *ptr++; - // That said, it is only used with gcc, so I assume it has optimization issues with llvm. + // That said, not sure how to write that in zig and guarantee it is branchless. // Thus using the longer form. const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left.*, right.*))) != utils.Ordering.GT; // TODO: double check this is branchless. @@ -119,8 +119,7 @@ inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { // Note there is a much simpler version here: // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; - // That said, it is only used with gcc, so I assume it has optimization issues with llvm. - // Thus using the longer form. + // That said, not sure how to write that in zig and guarantee it is branchless. const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left.*, right.*))) != utils.Ordering.GT; // TODO: double check this is branchless. const y = if (lte) element_width else 0; From 65ab733a6364bbb5fc9d1e9126820df35acae4d1 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 17:54:37 -0700 Subject: [PATCH 052/203] add perf comment/concern --- crates/compiler/builtins/bitcode/src/sort.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 2e815e4a1f0..1333818a400 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -22,6 +22,12 @@ pub fn quadsort( element_width: usize, copy: CopyFn, ) void { + // Note, knowing constant versions of element_width and copy could have huge perf gains. + // Hopefully llvm will essentially always do it via constant argument propagation and inlining. + // If not, we may want to generate `n` different version of this function with comptime. + // Then have our builtin dispatch to the correct version. + // llvm garbage collection would remove all other variants. + // Also, for numeric types, inlining the compare function can be a 2x perf gain. if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy); } else { From 1c90e01385b5386a4efc08ed50fa8501792ea17c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 18:30:18 -0700 Subject: [PATCH 053/203] add tiny_sort for 0 to 4 elements --- crates/compiler/builtins/bitcode/src/sort.zig | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 1333818a400..cfaac89575c 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -56,6 +56,89 @@ fn quadsort_direct( roc_panic("todo: quadsort", 0); } +// ================ Small Arrays ============================================== +// Below are functions for sorting 0 to 31 element arrays. + +/// Sort arrays of 0 to 4 elements. +fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; + const swap_ptr = @as([*]u8, @ptrCast(&buffer[0])); + + switch (len) { + 4 => { + var arr_ptr = array; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + + const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, arr_ptr, arr_ptr + element_width))) == utils.Ordering.GT; + if (gt) { + copy(swap_ptr, arr_ptr); + copy(arr_ptr, arr_ptr + element_width); + copy(arr_ptr + element_width, swap_ptr); + arr_ptr -= element_width; + + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + } + }, + 3 => { + var arr_ptr = array; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += element_width; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + arr_ptr = array; + swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + }, + 2 => { + swap_branchless(array, swap_ptr, cmp_data, cmp, element_width, copy); + }, + 1, 0 => { + return; + }, + else => { + unreachable; + }, + } +} + +test "tiny_sort" { + var arr = [4]i64{ 4, 2, 1, 3 }; + + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + tiny_sort(arr_ptr, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 2, 1, 4, 3 }; + + tiny_sort(arr_ptr, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 2, 3, 1, -1 }; + + tiny_sort(arr_ptr, 3, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr, [4]i64{ 1, 2, 3, -1 }); + + arr = [4]i64{ 2, 1, -1, -1 }; + + tiny_sort(arr_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + try testing.expectEqual(arr, [4]i64{ 1, 2, -1, -1 }); +} + +// ================ Primitives ================================================ +// Below are sorting primitives that attempt to be branchless. +// They all also are always inline for performance. +// The are the smallest fundamental unit. + /// Merge two neighboring sorted 4 element arrays into swap. inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { var left = ptr; From c3f09d57b04a34757d518014ccd13af2ce1daea2 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 18:34:19 -0700 Subject: [PATCH 054/203] cleanup test cases a bit --- crates/compiler/builtins/bitcode/src/sort.zig | 64 ++++++------------- 1 file changed, 18 insertions(+), 46 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index cfaac89575c..8b07a32dc3f 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -107,30 +107,23 @@ fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element } test "tiny_sort" { - var arr = [4]i64{ 4, 2, 1, 3 }; - + var arr: [4]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + arr = [4]i64{ 4, 2, 1, 3 }; tiny_sort(arr_ptr, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 1, 4, 3 }; - tiny_sort(arr_ptr, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 3, 1, -1 }; - tiny_sort(arr_ptr, 3, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, 3, -1 }); arr = [4]i64{ 2, 1, -1, -1 }; - tiny_sort(arr_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, -1, -1 }); } @@ -234,68 +227,56 @@ inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Compar } test "parity_merge_four" { - var arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; - var swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - + var arr: [8]i64 = undefined; + var swap: [8]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; + swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } test "parity_merge_two" { - var arr = [4]i64{ 1, 2, 3, 4 }; - var swap = [4]i64{ 0, 0, 0, 0 }; - + var arr: [4]i64 = undefined; + var swap: [4]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + arr = [4]i64{ 1, 2, 3, 4 }; + swap = [4]i64{ 0, 0, 0, 0 }; parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 3, 2, 4 }; swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 3, 4, 1, 2 }; swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 4, 1, 3 }; swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 4, 2, 3 }; swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); } @@ -336,31 +317,22 @@ test "tail_merge" { } test "swap" { - var arr = [2]i64{ 10, 20 }; + var arr: [2]i64 = undefined; + var swap: i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - var swap: i64 = 0; var swap_ptr = @as([*]u8, @ptrCast(&swap)); + arr = [2]i64{ 10, 20 }; swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [2]i64{ 10, 20 }); - try testing.expectEqual(arr[0], 10); - try testing.expectEqual(arr[1], 20); - - arr[0] = 77; - arr[1] = -12; - + arr = [2]i64{ 77, -12 }; swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [2]i64{ -12, 77 }); - try testing.expectEqual(arr[0], -12); - try testing.expectEqual(arr[1], 77); - - arr[0] = -22; - arr[1] = -22; - + arr = [2]i64{ -22, -22 }; swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - - try testing.expectEqual(arr[0], -22); - try testing.expectEqual(arr[1], -22); + try testing.expectEqual(arr, [2]i64{ -22, -22 }); } fn test_i64_compare(_: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { From edf797317f3993e6d569415fd92740e3fc2a9c95 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 19:20:22 -0700 Subject: [PATCH 055/203] cleanup calling the compare function --- crates/compiler/builtins/bitcode/src/sort.zig | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 8b07a32dc3f..ce84e610955 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -4,6 +4,10 @@ const testing = std.testing; const utils = @import("utils.zig"); const roc_panic = @import("panic.zig").panic_help; +const Ordering = utils.Ordering; +const GT = Ordering.GT; +const LT = Ordering.LT; +const EQ = Ordering.EQ; const Opaque = ?[*]u8; const CompareFn = *const fn (Opaque, Opaque, Opaque) callconv(.C) u8; const CopyFn = *const fn (Opaque, Opaque) callconv(.C) void; @@ -72,7 +76,7 @@ fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); arr_ptr -= element_width; - const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, arr_ptr, arr_ptr + element_width))) == utils.Ordering.GT; + const gt = @as(Ordering, @enumFromInt(cmp(cmp_data, arr_ptr, arr_ptr + element_width))) == GT; if (gt) { copy(swap_ptr, arr_ptr); copy(arr_ptr, arr_ptr + element_width); @@ -140,7 +144,7 @@ inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Comp head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) != utils.Ordering.GT; + const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; copy(swap_ptr, to_copy); @@ -150,7 +154,7 @@ inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Comp tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) == utils.Ordering.GT; + const gt = compare(cmp, cmp_data, left, right) == GT; to_copy = if (gt) left else right; copy(swap_ptr, to_copy); } @@ -161,7 +165,7 @@ inline fn parity_merge_two(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Compa var right = ptr + (2 * element_width); var swap_ptr = swap; head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) != utils.Ordering.GT; + const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; copy(swap_ptr, to_copy); @@ -169,7 +173,7 @@ inline fn parity_merge_two(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Compa right = ptr + (3 * element_width); swap_ptr = swap + (3 * element_width); tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left, right))) == utils.Ordering.GT; + const gt = compare(cmp, cmp_data, left, right) == GT; to_copy = if (gt) left else right; copy(swap_ptr, to_copy); } @@ -183,7 +187,7 @@ inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d // *ptd++ = cmp(ptl, ptr) <= 0 ? *ptl++ : *ptr++; // That said, not sure how to write that in zig and guarantee it is branchless. // Thus using the longer form. - const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left.*, right.*))) != utils.Ordering.GT; + const lte = compare(cmp, cmp_data, left.*, right.*) != GT; // TODO: double check this is branchless. const x = if (lte) element_width else 0; const not_x = if (lte) 0 else element_width; @@ -202,7 +206,7 @@ inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d // Note there is a much simpler version here: // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; // That said, not sure how to write that in zig and guarantee it is branchless. - const lte = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, left.*, right.*))) != utils.Ordering.GT; + const lte = compare(cmp, cmp_data, left.*, right.*) != GT; // TODO: double check this is branchless. const y = if (lte) element_width else 0; const not_y = if (lte) 0 else element_width; @@ -215,7 +219,7 @@ inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d /// Swaps the element at ptr with the element after it if the element is greater than the next. inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - const gt = @as(utils.Ordering, @enumFromInt(cmp(cmp_data, ptr, ptr + element_width))) == utils.Ordering.GT; + const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; // TODO: double check this is branchless. I would expect llvm to optimize this to be branchless. // But based on reading some comments in quadsort, llvm seems to prefer branches very often. const x = if (gt) element_width else 0; @@ -226,6 +230,10 @@ inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Compar copy(ptr + element_width, swap); } +inline fn compare(cmp: CompareFn, cmp_data: Opaque, lhs: [*]u8, rhs: [*]u8) Ordering { + return @as(Ordering, @enumFromInt(cmp(cmp_data, lhs, rhs))); +} + test "parity_merge_four" { var arr: [8]i64 = undefined; var swap: [8]i64 = undefined; From 8316e3f9cd5256202df6ddbb882fec3c5879198c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Mon, 22 Jul 2024 21:31:08 -0700 Subject: [PATCH 056/203] add twice_unguarded_insert --- crates/compiler/builtins/bitcode/src/sort.zig | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index ce84e610955..a87fcfa7018 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -63,6 +63,56 @@ fn quadsort_direct( // ================ Small Arrays ============================================== // Below are functions for sorting 0 to 31 element arrays. +// Inserts elements from offset to len into the sorted begining chunk of the array before offest. +// offset must be at least 2. +fn twice_unguarded_insert(array: [*]u8, offset: usize, len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; + const key_ptr = @as([*]u8, @ptrCast(&buffer[0])); + + for (offset..len) |i| { + var end_ptr = array + i * element_width; + var arr_ptr = end_ptr - element_width; + + const lte = compare(cmp, cmp_data, arr_ptr, end_ptr) != GT; + if (lte) { + continue; + } + + copy(key_ptr, end_ptr); + var gt = compare(cmp, cmp_data, array + element_width, key_ptr) == GT; + if (gt) { + var top = i - 1; + while (true) { + copy(end_ptr, arr_ptr); + end_ptr -= element_width; + arr_ptr -= element_width; + + top -= 1; + if (top == 0) { + break; + } + } + copy(end_ptr, key_ptr); + end_ptr -= element_width; + } else { + while (true) { + inline for (0..1) |_| { + copy(end_ptr, arr_ptr); + end_ptr -= element_width; + arr_ptr -= element_width; + } + gt = compare(cmp, cmp_data, arr_ptr, key_ptr) == GT; + if (!gt) { + break; + } + } + copy(end_ptr, end_ptr + element_width); + copy(end_ptr + element_width, key_ptr); + } + swap_branchless(end_ptr, key_ptr, cmp_data, cmp, element_width, copy); + } +} + /// Sort arrays of 0 to 4 elements. fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; @@ -110,6 +160,26 @@ fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element } } +test "twice_unguarded_insert" { + { + var arr = [7]i64{ 2, 3, 5, 6, 4, 1, 7 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + twice_unguarded_insert(arr_ptr, 4, 7, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); + } + { + var arr = [11]i64{ 1, 2, 5, 6, 7, 8, 9, 11, 10, 4, 3 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + twice_unguarded_insert(arr_ptr, 8, 11, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [11]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); + } + { + var arr = [23]i64{ 5, 6, 7, 8, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 10, 4, 3, 2, 16, 1, 9 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + twice_unguarded_insert(arr_ptr, 16, 23, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [23]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }); + } +} test "tiny_sort" { var arr: [4]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); From 539ab1daef1cf7a68141b2ecc593a71afe759da3 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 08:59:33 -0700 Subject: [PATCH 057/203] update comment on branchless generation --- crates/compiler/builtins/bitcode/src/sort.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index a87fcfa7018..b776f8be1a1 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -258,7 +258,7 @@ inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d // That said, not sure how to write that in zig and guarantee it is branchless. // Thus using the longer form. const lte = compare(cmp, cmp_data, left.*, right.*) != GT; - // TODO: double check this is branchless. + // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. const x = if (lte) element_width else 0; const not_x = if (lte) 0 else element_width; copy(dest.*, left.*); @@ -277,7 +277,7 @@ inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; // That said, not sure how to write that in zig and guarantee it is branchless. const lte = compare(cmp, cmp_data, left.*, right.*) != GT; - // TODO: double check this is branchless. + // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. const y = if (lte) element_width else 0; const not_y = if (lte) 0 else element_width; copy(dest.*, left.*); @@ -290,8 +290,7 @@ inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d /// Swaps the element at ptr with the element after it if the element is greater than the next. inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; - // TODO: double check this is branchless. I would expect llvm to optimize this to be branchless. - // But based on reading some comments in quadsort, llvm seems to prefer branches very often. + // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. const x = if (gt) element_width else 0; const y = if (gt) 0 else element_width; From 1d534517b3186cb2c37587f8ee3aeeea4130c260 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 10:32:39 -0700 Subject: [PATCH 058/203] update buffer size --- crates/compiler/builtins/bitcode/src/sort.zig | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index b776f8be1a1..bf57833a387 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -14,7 +14,15 @@ const CopyFn = *const fn (Opaque, Opaque) callconv(.C) void; const IncN = *const fn (?[*]u8, usize) callconv(.C) void; /// Any size larger than the max element buffer will be sorted indirectly via pointers. -const MAX_ELEMENT_BUFFER_SIZE: usize = 64; +/// TODO: tune this. +/// I did some basic basic testing on my M1 and x86 machines with the c version of fluxsort. +/// The best tradeoff point is not the clearest and heavily depends on machine specifics. +/// Generally speaking, the faster memcpy is and the larger the cache line, the larger this should be. +/// Also, to my surprise, sorting by pointer is more performant on short arrays than long arrays (probably reduces time of final gather to order main array). +/// Anyway, there seems to be a hard cut off were the direct sort cost suddenly gets way larger. +/// In my testing for long arrays, the cutoff seems to be around 96-128 bytes. +/// For sort arrays, the custoff seems to be around 64-96 bytes. +const MAX_ELEMENT_BUFFER_SIZE: usize = 96; pub fn quadsort( source_ptr: [*]u8, From 9702cae99d5d5070cbcd9fcb046c1da29d250ce8 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 10:46:27 -0700 Subject: [PATCH 059/203] improve branchless code gen --- crates/compiler/builtins/bitcode/src/sort.zig | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index bf57833a387..6087a483b06 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -261,18 +261,13 @@ inline fn parity_merge_two(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Compa /// Inlining will remove the extra level of pointer indirection here. /// It is just used to allow mutating the input pointers. inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - // Note there is a much simpler version here: + // Note equivalent c code: // *ptd++ = cmp(ptl, ptr) <= 0 ? *ptl++ : *ptr++; - // That said, not sure how to write that in zig and guarantee it is branchless. - // Thus using the longer form. - const lte = compare(cmp, cmp_data, left.*, right.*) != GT; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - const x = if (lte) element_width else 0; - const not_x = if (lte) 0 else element_width; - copy(dest.*, left.*); - left.* += x; - copy((dest.* + x), right.*); - right.* += not_x; + const lte = compare(cmp, cmp_data, left.*, right.*) != GT; + const from = if (lte) left else right; + copy(dest.*, from.*); + from.* += element_width; dest.* += element_width; } @@ -281,28 +276,23 @@ inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d /// Inlining will remove the extra level of pointer indirection here. /// It is just used to allow mutating the input pointers. inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - // Note there is a much simpler version here: + // Note equivalent c code: // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; - // That said, not sure how to write that in zig and guarantee it is branchless. - const lte = compare(cmp, cmp_data, left.*, right.*) != GT; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - const y = if (lte) element_width else 0; - const not_y = if (lte) 0 else element_width; - copy(dest.*, left.*); - left.* -= not_y; + const gt = compare(cmp, cmp_data, left.*, right.*) == GT; + const from = if (gt) left else right; + copy(dest.*, from.*); + from.* -= element_width; dest.* -= element_width; - copy((dest.* + y), right.*); - right.* -= y; } /// Swaps the element at ptr with the element after it if the element is greater than the next. inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - const x = if (gt) element_width else 0; - const y = if (gt) 0 else element_width; - - copy(swap, ptr + y); + const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; + var x = if (gt) element_width else 0; + const from = if (gt) ptr else ptr + element_width; + copy(swap, from); copy(ptr, ptr + x); copy(ptr + element_width, swap); } From eacc3771ebd3c89ec7a19652752ca3dea39c2961 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 14:05:58 -0700 Subject: [PATCH 060/203] implement latest version of tiny_sort --- crates/compiler/builtins/bitcode/src/sort.zig | 393 ++++++++++++------ 1 file changed, 268 insertions(+), 125 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 6087a483b06..d95f5b00168 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -71,96 +71,37 @@ fn quadsort_direct( // ================ Small Arrays ============================================== // Below are functions for sorting 0 to 31 element arrays. -// Inserts elements from offset to len into the sorted begining chunk of the array before offest. -// offset must be at least 2. -fn twice_unguarded_insert(array: [*]u8, offset: usize, len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +/// Sort arrays of 0 to 7 elements. +fn tiny_sort(array: [*]u8, len: usize, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; - const key_ptr = @as([*]u8, @ptrCast(&buffer[0])); - - for (offset..len) |i| { - var end_ptr = array + i * element_width; - var arr_ptr = end_ptr - element_width; - - const lte = compare(cmp, cmp_data, arr_ptr, end_ptr) != GT; - if (lte) { - continue; - } - - copy(key_ptr, end_ptr); - var gt = compare(cmp, cmp_data, array + element_width, key_ptr) == GT; - if (gt) { - var top = i - 1; - while (true) { - copy(end_ptr, arr_ptr); - end_ptr -= element_width; - arr_ptr -= element_width; - - top -= 1; - if (top == 0) { - break; - } - } - copy(end_ptr, key_ptr); - end_ptr -= element_width; - } else { - while (true) { - inline for (0..1) |_| { - copy(end_ptr, arr_ptr); - end_ptr -= element_width; - arr_ptr -= element_width; - } - gt = compare(cmp, cmp_data, arr_ptr, key_ptr) == GT; - if (!gt) { - break; - } - } - copy(end_ptr, end_ptr + element_width); - copy(end_ptr + element_width, key_ptr); - } - swap_branchless(end_ptr, key_ptr, cmp_data, cmp, element_width, copy); - } -} - -/// Sort arrays of 0 to 4 elements. -fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; - const swap_ptr = @as([*]u8, @ptrCast(&buffer[0])); + const tmp_ptr = @as([*]u8, @ptrCast(&buffer[0])); switch (len) { - 4 => { - var arr_ptr = array; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); - arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); - arr_ptr -= element_width; - - const gt = @as(Ordering, @enumFromInt(cmp(cmp_data, arr_ptr, arr_ptr + element_width))) == GT; - if (gt) { - copy(swap_ptr, arr_ptr); - copy(arr_ptr, arr_ptr + element_width); - copy(arr_ptr + element_width, swap_ptr); - arr_ptr -= element_width; - - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); - arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); - arr_ptr -= element_width; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); - } + 1, 0 => { + return; + }, + 2 => { + swap_branchless(array, tmp_ptr, cmp_data, cmp, element_width, copy); }, 3 => { var arr_ptr = array; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += element_width; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); - arr_ptr = array; - swap_branchless(arr_ptr, swap_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); }, - 2 => { - swap_branchless(array, swap_ptr, cmp_data, cmp, element_width, copy); + 4 => { + parity_swap_four(array, tmp_ptr, cmp_data, cmp, element_width, copy); }, - 1, 0 => { - return; + 5 => { + parity_swap_five(array, tmp_ptr, cmp_data, cmp, element_width, copy); + }, + 6 => { + parity_swap_six(array, tmp_ptr, swap, cmp_data, cmp, element_width, copy); + }, + 7 => { + parity_swap_seven(array, tmp_ptr, swap, cmp_data, cmp, element_width, copy); }, else => { unreachable; @@ -168,45 +109,241 @@ fn tiny_sort(array: [*]u8, len: usize, cmp_data: Opaque, cmp: CompareFn, element } } -test "twice_unguarded_insert" { +fn parity_swap_four(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var arr_ptr = array; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + if (gt) { + copy(tmp_ptr, arr_ptr); + copy(arr_ptr, arr_ptr + element_width); + copy(arr_ptr + element_width, tmp_ptr); + arr_ptr -= element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + } +} + +fn parity_swap_five(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var arr_ptr = array; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + const gt1 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + const gt2 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr = array; + + if (gt1 or gt2) { + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr = array; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + } +} + +fn parity_swap_six(array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var arr_ptr = array; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 3 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr = array; + { - var arr = [7]i64{ 2, 3, 5, 6, 4, 1, 7 }; + const lte = compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT; + if (lte) { + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 4 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + return; + } + } + + { + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + var x = if (gt) element_width else 0; + var not_x = if (!gt) element_width else 0; + copy(swap, arr_ptr + x); + copy(swap + element_width, arr_ptr + not_x); + copy(swap + 2 * element_width, arr_ptr + 2 * element_width); + arr_ptr += 4 * element_width; + } + { + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + var x = if (gt) element_width else 0; + var not_x = if (!gt) element_width else 0; + copy(swap + 4 * element_width, arr_ptr + x); + copy(swap + 5 * element_width, arr_ptr + not_x); + copy(swap + 3 * element_width, arr_ptr - element_width); + } + + arr_ptr = array; + var left = swap; + var right = swap + 3 * element_width; + + head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + + arr_ptr = array + 5 * element_width; + left = swap + 2 * element_width; + right = swap + 5 * element_width; + + tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + const gt = compare(cmp, cmp_data, left, right) == GT; + const from = if (gt) left else right; + copy(arr_ptr, from); +} + +fn parity_swap_seven(array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var arr_ptr = array; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= 3 * element_width; + const gt1 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + const gt2 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr += 2 * element_width; + const gt3 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr -= element_width; + + if (!(gt1 or gt2 or gt3)) + return; + + swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + arr_ptr = array; + + { + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + var x = if (gt) element_width else 0; + var not_x = if (!gt) element_width else 0; + copy(swap, arr_ptr + x); + copy(swap + element_width, arr_ptr + not_x); + copy(swap + 2 * element_width, arr_ptr + 2 * element_width); + arr_ptr += 3 * element_width; + } + { + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + var x = if (gt) element_width else 0; + var not_x = if (!gt) element_width else 0; + copy(swap + 3 * element_width, arr_ptr + x); + copy(swap + 4 * element_width, arr_ptr + not_x); + arr_ptr += 2 * element_width; + } + { + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + var x = if (gt) element_width else 0; + var not_x = if (!gt) element_width else 0; + copy(swap + 5 * element_width, arr_ptr + x); + copy(swap + 6 * element_width, arr_ptr + not_x); + } + + arr_ptr = array; + var left = swap; + var right = swap + 3 * element_width; + + head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + + arr_ptr = array + 6 * element_width; + left = swap + 2 * element_width; + right = swap + 6 * element_width; + + tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + const gt = compare(cmp, cmp_data, left, right) == GT; + const from = if (gt) left else right; + copy(arr_ptr, from); +} + +test "tiny_sort" { + var swap: [7]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + { + var arr: [7]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - twice_unguarded_insert(arr_ptr, 4, 7, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + + arr = [7]i64{ 3, 1, 2, 5, 4, 7, 6 }; + tiny_sort(arr_ptr, 7, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); + + arr = [7]i64{ 7, 6, 5, 4, 3, 2, 1 }; + tiny_sort(arr_ptr, 7, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); } { - var arr = [11]i64{ 1, 2, 5, 6, 7, 8, 9, 11, 10, 4, 3 }; + var arr: [6]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - twice_unguarded_insert(arr_ptr, 8, 11, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [11]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }); + + arr = [6]i64{ 3, 1, 2, 6, 4, 5 }; + tiny_sort(arr_ptr, 6, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); + + arr = [6]i64{ 6, 5, 4, 3, 2, 1 }; + tiny_sort(arr_ptr, 6, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); } { - var arr = [23]i64{ 5, 6, 7, 8, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 10, 4, 3, 2, 16, 1, 9 }; + var arr: [5]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - twice_unguarded_insert(arr_ptr, 16, 23, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [23]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }); - } -} -test "tiny_sort" { - var arr: [4]i64 = undefined; - var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - arr = [4]i64{ 4, 2, 1, 3 }; - tiny_sort(arr_ptr, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); + arr = [5]i64{ 2, 1, 4, 3, 5 }; + tiny_sort(arr_ptr, 5, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); - arr = [4]i64{ 2, 1, 4, 3 }; - tiny_sort(arr_ptr, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); + arr = [5]i64{ 5, 4, 3, 2, 1 }; + tiny_sort(arr_ptr, 5, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); + } + { + var arr: [4]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - arr = [4]i64{ 2, 3, 1, -1 }; - tiny_sort(arr_ptr, 3, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, 3, -1 }); + arr = [4]i64{ 4, 2, 1, 3 }; + tiny_sort(arr_ptr, 4, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); - arr = [4]i64{ 2, 1, -1, -1 }; - tiny_sort(arr_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, [4]i64{ 1, 2, -1, -1 }); + arr = [4]i64{ 2, 1, 4, 3 }; + tiny_sort(arr_ptr, 4, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); + } + { + var arr = [3]i64{ 2, 3, 1 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + tiny_sort(arr_ptr, 3, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [3]i64{ 1, 2, 3 }); + } + { + var arr = [2]i64{ 2, 1 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + tiny_sort(arr_ptr, 2, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [2]i64{ 1, 2 }); + } } // ================ Primitives ================================================ @@ -215,9 +352,9 @@ test "tiny_sort" { // The are the smallest fundamental unit. /// Merge two neighboring sorted 4 element arrays into swap. -inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - var left = ptr; - var right = ptr + (4 * element_width); +inline fn parity_merge_four(array: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var left = array; + var right = array + (4 * element_width); var swap_ptr = swap; head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); @@ -226,8 +363,8 @@ inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Comp var to_copy = if (lte) left else right; copy(swap_ptr, to_copy); - left = ptr + (3 * element_width); - right = ptr + (7 * element_width); + left = array + (3 * element_width); + right = array + (7 * element_width); swap_ptr = swap + (7 * element_width); tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); @@ -238,17 +375,17 @@ inline fn parity_merge_four(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Comp } /// Merge two neighboring sorted 2 element arrays into swap. -inline fn parity_merge_two(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { - var left = ptr; - var right = ptr + (2 * element_width); +inline fn parity_merge_two(array: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + var left = array; + var right = array + (2 * element_width); var swap_ptr = swap; head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; copy(swap_ptr, to_copy); - left = ptr + element_width; - right = ptr + (3 * element_width); + left = array + element_width; + right = array + (3 * element_width); swap_ptr = swap + (3 * element_width); tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; @@ -287,14 +424,20 @@ inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d } /// Swaps the element at ptr with the element after it if the element is greater than the next. -inline fn swap_branchless(ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +inline fn swap_branchless(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. + _ = swap_branchless_return_gt(ptr, tmp, cmp_data, cmp, element_width, copy); +} + +inline fn swap_branchless_return_gt(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) bool { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; var x = if (gt) element_width else 0; const from = if (gt) ptr else ptr + element_width; - copy(swap, from); + copy(tmp, from); copy(ptr, ptr + x); - copy(ptr + element_width, swap); + copy(ptr + element_width, tmp); + return gt; } inline fn compare(cmp: CompareFn, cmp_data: Opaque, lhs: [*]u8, rhs: [*]u8) Ordering { @@ -393,20 +536,20 @@ test "tail_merge" { test "swap" { var arr: [2]i64 = undefined; - var swap: i64 = undefined; + var tmp: i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - var swap_ptr = @as([*]u8, @ptrCast(&swap)); + var tmp_ptr = @as([*]u8, @ptrCast(&tmp)); arr = [2]i64{ 10, 20 }; - swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ 10, 20 }); arr = [2]i64{ 77, -12 }; - swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ -12, 77 }); arr = [2]i64{ -22, -22 }; - swap_branchless(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ -22, -22 }); } From f1a7772a12d1bae705b151f87d07a51fed4b93ff Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 14:32:09 -0700 Subject: [PATCH 061/203] improve branch generation with bools --- crates/compiler/builtins/bitcode/src/sort.zig | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index d95f5b00168..e03ea065b9a 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -136,12 +136,12 @@ fn parity_swap_five(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: Compare arr_ptr += 2 * element_width; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr -= element_width; - const gt1 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; - const gt2 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr = array; - if (gt1 or gt2) { + if (more_work != 0) { swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); @@ -222,14 +222,14 @@ fn parity_swap_seven(array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, cmp_data: Opaque arr_ptr += 2 * element_width; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr -= 3 * element_width; - const gt1 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; - const gt2 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; - const gt3 = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr -= element_width; - if (!(gt1 or gt2 or gt3)) + if (more_work == 0) return; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); @@ -429,7 +429,7 @@ inline fn swap_branchless(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: Compare _ = swap_branchless_return_gt(ptr, tmp, cmp_data, cmp, element_width, copy); } -inline fn swap_branchless_return_gt(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) bool { +inline fn swap_branchless_return_gt(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) u8 { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; var x = if (gt) element_width else 0; @@ -437,7 +437,7 @@ inline fn swap_branchless_return_gt(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cm copy(tmp, from); copy(ptr, ptr + x); copy(ptr + element_width, tmp); - return gt; + return @intFromBool(gt); } inline fn compare(cmp: CompareFn, cmp_data: Opaque, lhs: [*]u8, rhs: [*]u8) Ordering { From 5d2aaf7f9d95982006fe8a82385579106ddc8bef Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 15:22:12 -0700 Subject: [PATCH 062/203] add parity merge --- crates/compiler/builtins/bitcode/src/sort.zig | 69 ++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index e03ea065b9a..0a50060d003 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -69,10 +69,77 @@ fn quadsort_direct( } // ================ Small Arrays ============================================== -// Below are functions for sorting 0 to 31 element arrays. +// Below are functions for sorting under 31 element arrays. + +/// Merges two neighboring sorted arrays into dest. +/// Left must be equal to or 1 smaller than right. +fn parity_merge(dest: [*]u8, src: [*]u8, left_len: usize, right_len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + std.debug.assert(left_len == right_len or left_len == right_len - 1); + + var left_head = src; + var right_head = src + left_len * element_width; + var dest_head = dest; + + var left_tail = right_head - element_width; + var right_tail = left_tail + right_len * element_width; + var dest_tail = dest + (left_len + right_len - 1) * element_width; + + if (left_len < right_len) { + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + } + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + + var ll = left_len - 1; + while (ll != 0) : (ll -= 1) { + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + } + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); +} + +test "parity_merge" { + { + var dest: [8]i64 = undefined; + var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); + + var arr: [8]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; + dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge(dest_ptr, arr_ptr, 4, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + + arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; + dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge(dest_ptr, arr_ptr, 4, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + } + { + var dest: [9]i64 = undefined; + var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); + + var arr: [9]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [9]i64{ 1, 3, 5, 8, 2, 4, 6, 7, 9 }; + dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge(dest_ptr, arr_ptr, 4, 5, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + + arr = [9]i64{ 6, 7, 8, 9, 1, 2, 3, 4, 5 }; + dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge(dest_ptr, arr_ptr, 4, 5, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + } +} +// ================ Tiny Arrays ============================================== +// Below are functions for sorting 0 to 7 element arrays. /// Sort arrays of 0 to 7 elements. fn tiny_sort(array: [*]u8, len: usize, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { + std.debug.assert(len < 8); + var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; const tmp_ptr = @as([*]u8, @ptrCast(&buffer[0])); From 33e6dabeba5c77fe64bba16efe7ae9f8abef8daa Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 15:24:15 -0700 Subject: [PATCH 063/203] make function args multiline and visable --- crates/compiler/builtins/bitcode/src/sort.zig | 115 ++++++++++++++++-- 1 file changed, 103 insertions(+), 12 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 0a50060d003..efd80bf6c13 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -73,7 +73,16 @@ fn quadsort_direct( /// Merges two neighboring sorted arrays into dest. /// Left must be equal to or 1 smaller than right. -fn parity_merge(dest: [*]u8, src: [*]u8, left_len: usize, right_len: usize, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +fn parity_merge( + dest: [*]u8, + src: [*]u8, + left_len: usize, + right_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { std.debug.assert(left_len == right_len or left_len == right_len - 1); var left_head = src; @@ -137,7 +146,15 @@ test "parity_merge" { // Below are functions for sorting 0 to 7 element arrays. /// Sort arrays of 0 to 7 elements. -fn tiny_sort(array: [*]u8, len: usize, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +fn tiny_sort( + array: [*]u8, + len: usize, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { std.debug.assert(len < 8); var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; @@ -176,7 +193,14 @@ fn tiny_sort(array: [*]u8, len: usize, swap: [*]u8, cmp_data: Opaque, cmp: Compa } } -fn parity_swap_four(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +fn parity_swap_four( + array: [*]u8, + tmp_ptr: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; @@ -197,7 +221,14 @@ fn parity_swap_four(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: Compare } } -fn parity_swap_five(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +fn parity_swap_five( + array: [*]u8, + tmp_ptr: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; @@ -223,7 +254,15 @@ fn parity_swap_five(array: [*]u8, tmp_ptr: [*]u8, cmp_data: Opaque, cmp: Compare } } -fn parity_swap_six(array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +fn parity_swap_six( + array: [*]u8, + tmp_ptr: [*]u8, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += element_width; @@ -281,7 +320,15 @@ fn parity_swap_six(array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, copy(arr_ptr, from); } -fn parity_swap_seven(array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +fn parity_swap_seven( + array: [*]u8, + tmp_ptr: [*]u8, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); arr_ptr += 2 * element_width; @@ -419,7 +466,14 @@ test "tiny_sort" { // The are the smallest fundamental unit. /// Merge two neighboring sorted 4 element arrays into swap. -inline fn parity_merge_four(array: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +inline fn parity_merge_four( + array: [*]u8, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { var left = array; var right = array + (4 * element_width); var swap_ptr = swap; @@ -442,7 +496,14 @@ inline fn parity_merge_four(array: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Co } /// Merge two neighboring sorted 2 element arrays into swap. -inline fn parity_merge_two(array: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +inline fn parity_merge_two( + array: [*]u8, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { var left = array; var right = array + (2 * element_width); var swap_ptr = swap; @@ -464,7 +525,15 @@ inline fn parity_merge_two(array: [*]u8, swap: [*]u8, cmp_data: Opaque, cmp: Com /// Will increment both dest and the smaller element ptr to their next index. /// Inlining will remove the extra level of pointer indirection here. /// It is just used to allow mutating the input pointers. -inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +inline fn head_branchless_merge( + dest: *[*]u8, + left: *[*]u8, + right: *[*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { // Note equivalent c code: // *ptd++ = cmp(ptl, ptr) <= 0 ? *ptl++ : *ptr++; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. @@ -479,7 +548,15 @@ inline fn head_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d /// Will decrement both dest and the smaller element ptr to their previous index. /// Inlining will remove the extra level of pointer indirection here. /// It is just used to allow mutating the input pointers. -inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +inline fn tail_branchless_merge( + dest: *[*]u8, + left: *[*]u8, + right: *[*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { // Note equivalent c code: // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. @@ -491,12 +568,26 @@ inline fn tail_branchless_merge(dest: *[*]u8, left: *[*]u8, right: *[*]u8, cmp_d } /// Swaps the element at ptr with the element after it if the element is greater than the next. -inline fn swap_branchless(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) void { +inline fn swap_branchless( + ptr: [*]u8, + tmp: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. _ = swap_branchless_return_gt(ptr, tmp, cmp_data, cmp, element_width, copy); } -inline fn swap_branchless_return_gt(ptr: [*]u8, tmp: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, copy: CopyFn) u8 { +inline fn swap_branchless_return_gt( + ptr: [*]u8, + tmp: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) u8 { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; var x = if (gt) element_width else 0; From a7823c2164977158478afe4c30640806c02966a5 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 15:51:53 -0700 Subject: [PATCH 064/203] add tailswap for 31 or less elements --- crates/compiler/builtins/bitcode/src/sort.zig | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index efd80bf6c13..188eee732b6 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -69,7 +69,50 @@ fn quadsort_direct( } // ================ Small Arrays ============================================== -// Below are functions for sorting under 31 element arrays. +// Below are functions for sorting under 32 element arrays. + +/// Uses swap space to sort the tail of an array. +/// The array should be under 32 elements in length. +fn tail_swap( + array: [*]u8, + len: usize, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + std.debug.assert(len < 32); + + if (len < 8) { + tiny_sort(array, len, swap, cmp_data, cmp, element_width, copy); + return; + } + + const half1 = len / 2; + const quad1 = half1 / 2; + const quad2 = half1 - quad1; + const half2 = len - half1; + const quad3 = half2 / 2; + const quad4 = half2 - quad3; + + var arr_ptr = array; + tail_swap(arr_ptr, quad1, swap, cmp_data, cmp, element_width, copy); + arr_ptr += quad1 * element_width; + tail_swap(arr_ptr, quad2, swap, cmp_data, cmp, element_width, copy); + arr_ptr += quad2 * element_width; + tail_swap(arr_ptr, quad3, swap, cmp_data, cmp, element_width, copy); + arr_ptr += quad3 * element_width; + tail_swap(arr_ptr, quad4, swap, cmp_data, cmp, element_width, copy); + + if (compare(cmp, cmp_data, array + (quad1 - 1) * element_width, array + quad1 * element_width) != GT and compare(cmp, cmp_data, array + (half1 - 1) * element_width, array + half1 * element_width) != GT and compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr) != GT) { + return; + } + + parity_merge(swap, array, quad1, quad2, cmp_data, cmp, element_width, copy); + parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp_data, cmp, element_width, copy); + parity_merge(array, swap, half1, half2, cmp_data, cmp, element_width, copy); +} /// Merges two neighboring sorted arrays into dest. /// Left must be equal to or 1 smaller than right. @@ -106,6 +149,27 @@ fn parity_merge( tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); } +test "tail_swap" { + var swap: [31]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + var arr: [31]i64 = undefined; + var expected: [31]i64 = undefined; + for (0..31) |i| { + arr[i] = @intCast(i + 1); + expected[i] = @intCast(i + 1); + } + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + for (0..10) |seed| { + var rng = std.rand.DefaultPrng.init(seed); + rng.random().shuffle(i64, arr[0..]); + + tail_swap(arr_ptr, 31, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + } +} + test "parity_merge" { { var dest: [8]i64 = undefined; From f631bae67a1d8a24064d720248a98b156aaaab67 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 16:43:15 -0700 Subject: [PATCH 065/203] add quad_reversal --- crates/compiler/builtins/bitcode/src/sort.zig | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 188eee732b6..34c168c25fe 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -23,6 +23,11 @@ const IncN = *const fn (?[*]u8, usize) callconv(.C) void; /// In my testing for long arrays, the cutoff seems to be around 96-128 bytes. /// For sort arrays, the custoff seems to be around 64-96 bytes. const MAX_ELEMENT_BUFFER_SIZE: usize = 96; +const BufferType = [MAX_ELEMENT_BUFFER_SIZE]u8; +const BufferAlign = @alignOf(u128); +comptime { + std.debug.assert(MAX_ELEMENT_BUFFER_SIZE % BufferAlign == 0); +} pub fn quadsort( source_ptr: [*]u8, @@ -68,6 +73,74 @@ fn quadsort_direct( roc_panic("todo: quadsort", 0); } +// ================ 32 Element Blocks ========================================= + +fn quad_reversal( + start: [*]u8, + end: [*]u8, + element_width: usize, + copy: CopyFn, +) void { + var buffer1: BufferType align(BufferAlign) = undefined; + var buffer2: BufferType align(BufferAlign) = undefined; + + const tmp1_ptr = @as([*]u8, @ptrCast(&buffer1[0])); + const tmp2_ptr = @as([*]u8, @ptrCast(&buffer2[0])); + + var loops = (@intFromPtr(end) - @intFromPtr(start)) / (element_width * 2); + + var h1_start = start; + var h1_end = start + loops * element_width; + var h2_start = end - loops * element_width; + var h2_end = end; + + if (loops % 2 == 0) { + copy(tmp2_ptr, h1_end); + copy(h1_end, h2_start); + h1_end -= element_width; + copy(h2_start, tmp2_ptr); + h2_start += element_width; + loops -= 1; + } + + loops /= 2; + + while (true) { + copy(tmp1_ptr, h1_start); + copy(h1_start, h2_end); + h1_start += element_width; + copy(h2_end, tmp1_ptr); + h2_end -= element_width; + + copy(tmp2_ptr, h1_end); + copy(h1_end, h2_start); + h1_end -= element_width; + copy(h2_start, tmp2_ptr); + h2_start += element_width; + + if (loops == 0) + break; + loops -= 1; + } +} + +test "quad_reversal" { + { + var arr = [8]i64{ 8, 7, 6, 5, 4, 3, 2, 1 }; + var start_ptr = @as([*]u8, @ptrCast(&arr[0])); + var end_ptr = @as([*]u8, @ptrCast(&arr[7])); + quad_reversal(start_ptr, end_ptr, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + } + { + var arr = [9]i64{ 9, 8, 7, 6, 5, 4, 3, 2, 1 }; + var start_ptr = @as([*]u8, @ptrCast(&arr[0])); + var end_ptr = @as([*]u8, @ptrCast(&arr[8])); + quad_reversal(start_ptr, end_ptr, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + } +} + // ================ Small Arrays ============================================== // Below are functions for sorting under 32 element arrays. @@ -206,7 +279,8 @@ test "parity_merge" { try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); } } -// ================ Tiny Arrays ============================================== + +// ================ Tiny Arrays =============================================== // Below are functions for sorting 0 to 7 element arrays. /// Sort arrays of 0 to 7 elements. @@ -221,7 +295,7 @@ fn tiny_sort( ) void { std.debug.assert(len < 8); - var buffer: [MAX_ELEMENT_BUFFER_SIZE]u8 = undefined; + var buffer: BufferType align(BufferAlign) = undefined; const tmp_ptr = @as([*]u8, @ptrCast(&buffer[0])); switch (len) { From 87b339d09ab639a28eee09073d59c48d986039ad Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 16:52:50 -0700 Subject: [PATCH 066/203] add quad_swap_merge --- crates/compiler/builtins/bitcode/src/sort.zig | 134 +++++++++++------- 1 file changed, 86 insertions(+), 48 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 34c168c25fe..e5ca4e0de4a 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -75,6 +75,22 @@ fn quadsort_direct( // ================ 32 Element Blocks ========================================= +/// Merge 4 sorted arrays of length 2 into a sorted array of length 8 using swap space. +fn quad_swap_merge( + array: [*]u8, + swap: [*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + parity_merge_two(array, swap, cmp_data, cmp, element_width, copy); + parity_merge_two(array + 4 * element_width, swap + 4 * element_width, cmp_data, cmp, element_width, copy); + + parity_merge_four(swap, array, cmp_data, cmp, element_width, copy); +} + +// Reverse values from start to end. fn quad_reversal( start: [*]u8, end: [*]u8, @@ -124,6 +140,28 @@ fn quad_reversal( } } +test "quad_swap_merge" { + var arr: [8]i64 = undefined; + var swap: [8]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; + swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + quad_swap_merge(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + + arr = [8]i64{ 5, 7, 1, 3, 6, 8, 2, 4 }; + swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + quad_swap_merge(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + + arr = [8]i64{ 1, 8, 3, 4, 5, 6, 2, 7 }; + swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + quad_swap_merge(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); +} + test "quad_reversal" { { var arr = [8]i64{ 8, 7, 6, 5, 4, 3, 2, 1 }; @@ -603,10 +641,10 @@ test "tiny_sort" { // They all also are always inline for performance. // The are the smallest fundamental unit. -/// Merge two neighboring sorted 4 element arrays into swap. +/// Merge two neighboring sorted 4 element arrays into dest. inline fn parity_merge_four( array: [*]u8, - swap: [*]u8, + dest: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, @@ -614,29 +652,29 @@ inline fn parity_merge_four( ) void { var left = array; var right = array + (4 * element_width); - var swap_ptr = swap; - head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + var dest_ptr = dest; + head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; - copy(swap_ptr, to_copy); + copy(dest_ptr, to_copy); left = array + (3 * element_width); right = array + (7 * element_width); - swap_ptr = swap + (7 * element_width); - tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + dest_ptr = dest + (7 * element_width); + tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; to_copy = if (gt) left else right; - copy(swap_ptr, to_copy); + copy(dest_ptr, to_copy); } -/// Merge two neighboring sorted 2 element arrays into swap. +/// Merge two neighboring sorted 2 element arrays into dest. inline fn parity_merge_two( array: [*]u8, - swap: [*]u8, + dest: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, @@ -644,19 +682,19 @@ inline fn parity_merge_two( ) void { var left = array; var right = array + (2 * element_width); - var swap_ptr = swap; - head_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + var dest_ptr = dest; + head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; - copy(swap_ptr, to_copy); + copy(dest_ptr, to_copy); left = array + element_width; right = array + (3 * element_width); - swap_ptr = swap + (3 * element_width); - tail_branchless_merge(&swap_ptr, &left, &right, cmp_data, cmp, element_width, copy); + dest_ptr = dest + (3 * element_width); + tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; to_copy = if (gt) left else right; - copy(swap_ptr, to_copy); + copy(dest_ptr, to_copy); } /// Moves the smaller element from left and rigth to dest. @@ -742,56 +780,56 @@ inline fn compare(cmp: CompareFn, cmp_data: Opaque, lhs: [*]u8, rhs: [*]u8) Orde test "parity_merge_four" { var arr: [8]i64 = undefined; - var swap: [8]i64 = undefined; + var dest: [8]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; - swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge_four(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; - swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge_four(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; - swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); + dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge_four(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } test "parity_merge_two" { var arr: [4]i64 = undefined; - var swap: [4]i64 = undefined; + var dest: [4]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); arr = [4]i64{ 1, 2, 3, 4 }; - swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + dest = [4]i64{ 0, 0, 0, 0 }; + parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 3, 2, 4 }; - swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + dest = [4]i64{ 0, 0, 0, 0 }; + parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 3, 4, 1, 2 }; - swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + dest = [4]i64{ 0, 0, 0, 0 }; + parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 4, 1, 3 }; - swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + dest = [4]i64{ 0, 0, 0, 0 }; + parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 4, 2, 3 }; - swap = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(swap, [4]i64{ 1, 2, 3, 4 }); + dest = [4]i64{ 0, 0, 0, 0 }; + parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); } test "head_merge" { From 0f56b98bc6186881dffdcca56e50e4ef9243fd7b Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 16:54:27 -0700 Subject: [PATCH 067/203] reorder args to put destination first --- crates/compiler/builtins/bitcode/src/sort.zig | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index e5ca4e0de4a..5afc2862a8f 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -84,10 +84,10 @@ fn quad_swap_merge( element_width: usize, copy: CopyFn, ) void { - parity_merge_two(array, swap, cmp_data, cmp, element_width, copy); - parity_merge_two(array + 4 * element_width, swap + 4 * element_width, cmp_data, cmp, element_width, copy); + parity_merge_two(swap, array, cmp_data, cmp, element_width, copy); + parity_merge_two(swap + 4 * element_width, array + 4 * element_width, cmp_data, cmp, element_width, copy); - parity_merge_four(swap, array, cmp_data, cmp, element_width, copy); + parity_merge_four(array, swap, cmp_data, cmp, element_width, copy); } // Reverse values from start to end. @@ -643,8 +643,8 @@ test "tiny_sort" { /// Merge two neighboring sorted 4 element arrays into dest. inline fn parity_merge_four( - array: [*]u8, dest: [*]u8, + array: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, @@ -673,8 +673,8 @@ inline fn parity_merge_four( /// Merge two neighboring sorted 2 element arrays into dest. inline fn parity_merge_two( - array: [*]u8, dest: [*]u8, + array: [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, @@ -786,17 +786,17 @@ test "parity_merge_four" { arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } @@ -808,27 +808,27 @@ test "parity_merge_two" { arr = [4]i64{ 1, 2, 3, 4 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 3, 2, 4 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 3, 4, 1, 2 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 4, 1, 3 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 4, 2, 3 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(arr_ptr, dest_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); } From 7164d74ad1035d610ff9d0c527091bc7292f642d Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 18:16:22 -0700 Subject: [PATCH 068/203] add cross merge --- crates/compiler/builtins/bitcode/src/sort.zig | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 5afc2862a8f..dfafafbc186 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -72,8 +72,189 @@ fn quadsort_direct( _ = source_ptr; roc_panic("todo: quadsort", 0); } +// ================ Quad Merge Support ======================================== + +// Cross merge attempts to merge two arrays in chunks of multiple elements. +fn cross_merge( + dest: [*]u8, + src: [*]u8, + left_len: usize, + right_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + var left_head = src; + var right_head = src + left_len * element_width; + var left_tail = right_head - element_width; + var right_tail = left_tail + right_len * element_width; + + // If the data looks too random and the sizes are similar, + // fallback to the branchless parity merge. + if (left_len + 1 >= right_len and right_len + 1 >= left_len and left_len >= 32) { + const offset = 15 * element_width; + if (compare(cmp, cmp_data, left_head + offset, right_head) == GT and compare(cmp, cmp_data, left_head, right_head + offset) != GT and compare(cmp, cmp_data, left_tail, right_tail - offset) == GT and compare(cmp, cmp_data, left_tail - offset, right_tail) != GT) { + parity_merge(dest, src, left_len, right_len, cmp_data, cmp, element_width, copy); + return; + } + } + + var dest_head = dest; + var dest_tail = dest + (left_len + right_len - 1) * element_width; + + outer: while (true) { + if (@intFromPtr(left_tail) - @intFromPtr(left_head) > 8 * element_width) { + // 8 elements all less than or equal to and can be moved together. + while (compare(cmp, cmp_data, left_head + 7 * element_width, right_head) != GT) { + // TODO: Should this actually be a memcpy? + // Memcpy won't know the size until runtime but it is 1 call instead of 8. + inline for (0..8) |_| { + copy(dest_head, left_head); + dest_head += element_width; + left_head += element_width; + } + if (@intFromPtr(left_tail) - @intFromPtr(left_head) <= 8 * element_width) + continue :outer; + } + + // Attempt to do the same from the tail. + // 8 elements all greater than and can be moved together. + while (compare(cmp, cmp_data, left_tail - 7 * element_width, right_tail) == GT) { + // TODO: Should this actually be a memcpy? + // Memcpy won't know the size until runtime but it is 1 call instead of 8. + inline for (0..8) |_| { + copy(dest_tail, left_tail); + dest_tail -= element_width; + left_tail -= element_width; + } + if (@intFromPtr(left_tail) - @intFromPtr(left_head) <= 8 * element_width) + continue :outer; + } + } + + // Attempt to do the same for the right list. + if (@intFromPtr(right_tail) - @intFromPtr(right_head) > 8 * element_width) { + // left greater than 8 elements right and can be moved together. + while (compare(cmp, cmp_data, left_head, right_head + 7 * element_width) == GT) { + // TODO: Should this actually be a memcpy? + // Memcpy won't know the size until runtime but it is 1 call instead of 8. + inline for (0..8) |_| { + copy(dest_head, right_head); + dest_head += element_width; + right_head += element_width; + } + if (@intFromPtr(right_tail) - @intFromPtr(right_head) <= 8 * element_width) + continue :outer; + } + + // Attempt to do the same from the tail. + // left less than or equalt to 8 elements right and can be moved together. + while (compare(cmp, cmp_data, left_tail, right_tail - 7 * element_width) != GT) { + // TODO: Should this actually be a memcpy? + // Memcpy won't know the size until runtime but it is 1 call instead of 8. + inline for (0..8) |_| { + copy(dest_tail, right_tail); + dest_tail -= element_width; + right_tail -= element_width; + } + if (@intFromPtr(right_tail) - @intFromPtr(right_head) <= 8 * element_width) + continue :outer; + } + } + + if (@intFromPtr(dest_tail) - @intFromPtr(dest_head) < 16 * element_width) + break; + + // Large enough to warrent a two way merge. + var loops: usize = 8; + while (true) { + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + + loops -= 1; + if (loops == 0) + break; + } + } + + // Clean up tail. + while (@intFromPtr(left_head) <= @intFromPtr(left_tail) and @intFromPtr(right_head) <= @intFromPtr(right_tail)) { + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + } + while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { + copy(dest_head, left_head); + dest_head += element_width; + left_head += element_width; + } + while (@intFromPtr(right_head) <= @intFromPtr(right_tail)) { + copy(dest_head, right_head); + dest_head += element_width; + right_head += element_width; + } +} + +test "cross_merge" { + var expected: [64]i64 = undefined; + for (0..64) |i| { + expected[i] = @intCast(i + 1); + } + + var src: [64]i64 = undefined; + var dest: [64]i64 = undefined; + var src_ptr = @as([*]u8, @ptrCast(&src[0])); + var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); + + // Opitimal case, ordered but swapped + for (0..32) |i| { + src[i] = @intCast(i + 33); + } + for (0..32) |i| { + src[i + 32] = @intCast(i + 1); + } + cross_merge(dest_ptr, src_ptr, 32, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, expected); + + // will fallback, every other + for (0..32) |i| { + src[i * 2] = @intCast(i * 2 + 1); + src[i * 2 + 1] = @intCast(i * 2 + 2); + } + cross_merge(dest_ptr, src_ptr, 32, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, expected); + + // super uneven + for (0..20) |i| { + src[i] = @intCast(i + 45); + } + for (0..44) |i| { + src[i + 20] = @intCast(i + 1); + } + cross_merge(dest_ptr, src_ptr, 20, 44, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, expected); + + // chunks + for (0..16) |i| { + src[i] = @intCast(i + 17); + } + for (0..16) |i| { + src[i + 16] = @intCast(i + 49); + } + for (0..16) |i| { + src[i + 32] = @intCast(i + 1); + } + for (0..16) |i| { + src[i + 48] = @intCast(i + 33); + } + cross_merge(dest_ptr, src_ptr, 32, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(dest, expected); +} // ================ 32 Element Blocks ========================================= +// This is basically a fast path to avoid `roc_alloc` for very sort arrays. + +// TODO: Implement quad_swap here. +// It deals with 32 elements without a large allocation. /// Merge 4 sorted arrays of length 2 into a sorted array of length 8 using swap space. fn quad_swap_merge( From 21132a6740d14737c6fecb0e94eac888872515ba Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 18:38:48 -0700 Subject: [PATCH 069/203] add quad merge blocks --- crates/compiler/builtins/bitcode/src/sort.zig | 89 ++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index dfafafbc186..44a21ef7a00 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -74,7 +74,58 @@ fn quadsort_direct( } // ================ Quad Merge Support ======================================== -// Cross merge attempts to merge two arrays in chunks of multiple elements. +/// Merges 4 even sized blocks of sorted elements. +fn quad_merge_block( + array: [*]u8, + swap: [*]u8, + block_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + const block_x_2 = 2 * block_len; + + const block1 = array; + const block2 = block1 + block_len * element_width; + const block3 = block2 + block_len * element_width; + const block4 = block3 + block_len * element_width; + + const in_order_1_2: u2 = @intFromBool(compare(cmp, cmp_data, block2 - element_width, block2) != GT); + const in_order_3_4: u2 = @intFromBool(compare(cmp, cmp_data, block4 - element_width, block4) != GT); + + switch (in_order_1_2 | (in_order_3_4 << 1)) { + 0 => { + // Nothing sorted. Just run merges on both. + cross_merge(swap, array, block_len, block_len, cmp_data, cmp, element_width, copy); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp_data, cmp, element_width, copy); + }, + 1 => { + // First half sorted already. + @memcpy(swap[0..(element_width * block_x_2)], array[0..(element_width * block_x_2)]); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp_data, cmp, element_width, copy); + }, + 2 => { + // Second half sorted already. + cross_merge(swap, array, block_len, block_len, cmp_data, cmp, element_width, copy); + @memcpy((swap + element_width * block_x_2)[0..(element_width * block_x_2)], block3[0..(element_width * block_x_2)]); + }, + 3 => { + const in_order_2_3 = compare(cmp, cmp_data, block3 - element_width, block3) != GT; + if (in_order_2_3) + // Lucky, all sorted. + return; + + // Copy everything into swap to merge back into this array. + @memcpy(swap[0..(element_width * block_x_2 * 2)], array[0..(element_width * block_x_2 * 2)]); + }, + } + + // Merge 2 larger blocks. + cross_merge(array, swap, block_x_2, block_x_2, cmp_data, cmp, element_width, copy); +} + +/// Cross merge attempts to merge two arrays in chunks of multiple elements. fn cross_merge( dest: [*]u8, src: [*]u8, @@ -194,6 +245,40 @@ fn cross_merge( } } +test "quad_merge_block" { + const expected = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; + + var arr: [8]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [8]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + // case 0 - totally unsorted + arr = [8]i64{ 7, 8, 5, 6, 3, 4, 1, 2 }; + quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // case 1 - first half sorted + arr = [8]i64{ 5, 6, 7, 8, 3, 4, 1, 2 }; + quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // case 2 - second half sorted + arr = [8]i64{ 7, 8, 5, 6, 1, 2, 3, 4 }; + quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // case 3 both haves sorted + arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; + quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // case 3 - lucky, sorted + arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; + quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); +} + test "cross_merge" { var expected: [64]i64 = undefined; for (0..64) |i| { @@ -271,7 +356,7 @@ fn quad_swap_merge( parity_merge_four(array, swap, cmp_data, cmp, element_width, copy); } -// Reverse values from start to end. +/// Reverse values from start to end. fn quad_reversal( start: [*]u8, end: [*]u8, From 2455c1dd05cee3f46aa6763bd63830531bac7e2a Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 18:41:31 -0700 Subject: [PATCH 070/203] update todos --- crates/compiler/builtins/bitcode/src/sort.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 44a21ef7a00..e631e77ff9f 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -74,6 +74,8 @@ fn quadsort_direct( } // ================ Quad Merge Support ======================================== +// TODO: quad_merge, requires tail merge first. + /// Merges 4 even sized blocks of sorted elements. fn quad_merge_block( array: [*]u8, @@ -338,7 +340,7 @@ test "cross_merge" { // ================ 32 Element Blocks ========================================= // This is basically a fast path to avoid `roc_alloc` for very sort arrays. -// TODO: Implement quad_swap here. +// TODO: quad_swap, requires tail merge first. // It deals with 32 elements without a large allocation. /// Merge 4 sorted arrays of length 2 into a sorted array of length 8 using swap space. From ea0063b99296ca318b5e4aca1731598103a35cb1 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 20:59:29 -0700 Subject: [PATCH 071/203] add partial forward merge --- crates/compiler/builtins/bitcode/src/sort.zig | 171 +++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index e631e77ff9f..ecac9763378 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -72,6 +72,175 @@ fn quadsort_direct( _ = source_ptr; roc_panic("todo: quadsort", 0); } + +// ================ Unbalanced Merges ========================================= + +/// Merges a full left block with a smaller than block size right chunk. +fn partial_forward_merge( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + block_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + std.debug.assert(swap_len >= block_len); + + if (len == block_len) { + // Just a single block, already done. + return; + } + + var right_head = array + block_len * element_width; + var right_tail = array + (len - 1) * element_width; + + if (compare(cmp, cmp_data, right_head - element_width, right_head) != GT) { + // Luck case, blocks happen to be sorted. + return; + } + + @memcpy(swap[0..(element_width * block_len)], array[0..(element_width * block_len)]); + + var left_head = swap; + var left_tail = swap + (block_len - 1) * element_width; + + var dest_head = array; + // Attempt to merge 2 elements a time from head then tail. + while (@intFromPtr(left_head) < @intFromPtr(left_tail) - element_width and @intFromPtr(right_head) < @intFromPtr(right_tail) - element_width) { + // Note: I am not sure how to get the same generation as the original C. + // This implementation has an extra function call here. + // The C use `goto` to implement the two tail recursive functions below inline. + const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp_data, cmp, element_width, copy); + if (break_loop) + break; + + // Couldn't move two elements, do a cross swap and continue. + const lte = compare(cmp, cmp_data, left_head, right_head) != GT; + var x = if (lte) element_width else 0; + var not_x = if (!lte) element_width else 0; + copy(dest_head + x, right_head); + right_head += element_width; + copy(dest_head + not_x, left_head); + left_head += element_width; + dest_head += 2 * element_width; + + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + } + + // Deal with tail. + while (@intFromPtr(left_head) <= @intFromPtr(left_tail) and @intFromPtr(right_head) <= @intFromPtr(right_tail)) { + head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + } + while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { + copy(dest_head, left_head); + dest_head += element_width; + left_head += element_width; + } +} + +// The following two functions are exactly the same but with the if blocks swapped. +// They hot loop on one side until it fails, then switch to the other list. + +fn partial_forward_merge_right_head_2( + dest: *[*]u8, + left_head: *[*]u8, + left_tail: *[*]u8, + right_head: *[*]u8, + right_tail: *[*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) bool { + if (compare(cmp, cmp_data, left_head.*, right_head.* + element_width) == GT) { + inline for (0..2) |_| { + copy(dest.*, right_head.*); + dest.* += element_width; + right_head.* += element_width; + } + if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { + return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + if (compare(cmp, cmp_data, left_head.* + element_width, right_head.*) != GT) { + inline for (0..2) |_| { + copy(dest.*, left_head.*); + dest.* += element_width; + left_head.* += element_width; + } + if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { + return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + return false; +} + +fn partial_forward_merge_left_head_2( + dest: *[*]u8, + left_head: *[*]u8, + left_tail: *[*]u8, + right_head: *[*]u8, + right_tail: *[*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) bool { + if (compare(cmp, cmp_data, left_head.* + element_width, right_head.*) != GT) { + inline for (0..2) |_| { + copy(dest.*, left_head.*); + dest.* += element_width; + left_head.* += element_width; + } + if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { + return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + if (compare(cmp, cmp_data, left_head.*, right_head.* + element_width) == GT) { + inline for (0..2) |_| { + copy(dest.*, right_head.*); + dest.* += element_width; + right_head.* += element_width; + } + if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { + return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + return false; +} + +test "partial_forward_merge" { + const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; + partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); +} + // ================ Quad Merge Support ======================================== // TODO: quad_merge, requires tail merge first. @@ -338,8 +507,8 @@ test "cross_merge" { } // ================ 32 Element Blocks ========================================= -// This is basically a fast path to avoid `roc_alloc` for very sort arrays. +/// This is basically a fast path to avoid `roc_alloc` for very sort arrays. // TODO: quad_swap, requires tail merge first. // It deals with 32 elements without a large allocation. From eb8c91775fa578d31bef54671b33f3204c1390c9 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 22:10:47 -0700 Subject: [PATCH 072/203] add partial backwards merge --- crates/compiler/builtins/bitcode/src/sort.zig | 301 +++++++++++++++++- 1 file changed, 288 insertions(+), 13 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index ecac9763378..a8bc128d36e 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -76,6 +76,215 @@ fn quadsort_direct( // ================ Unbalanced Merges ========================================= /// Merges a full left block with a smaller than block size right chunk. +/// The merge goes from tail to head. +fn partial_backwards_merge( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + block_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + std.debug.assert(swap_len >= block_len); + + if (len == block_len) { + // Just a single block, already done. + return; + } + + var left_tail = array + (block_len - 1) * element_width; + var dest_tail = array + (len - 1) * element_width; + + if (compare(cmp, cmp_data, left_tail, left_tail + element_width) != GT) { + // Lucky case, blocks happen to be sorted. + return; + } + + const right_len = len - block_len; + if (len <= swap_len and right_len >= 64) { + // Large remaining merge and we have enough space to just do it in swap. + + cross_merge(swap, array, block_len, right_len, cmp_data, cmp, element_width, copy); + + @memcpy(array[0..(element_width * len)], swap[0..(element_width * len)]); + + return; + } + + @memcpy(swap[0..(element_width * right_len)], (array + block_len * element_width)[0..(element_width * right_len)]); + + var right_tail = swap + (right_len - 1) * element_width; + + // For backards, we first try to do really large chunks, of 16 elements. + outer: while (@intFromPtr(left_tail) > @intFromPtr(array + 16 * element_width) and @intFromPtr(right_tail) > @intFromPtr(swap + 16 * element_width)) { + while (compare(cmp, cmp_data, left_tail, right_tail - 15 * element_width) != GT) { + inline for (0..16) |_| { + copy(dest_tail, right_tail); + dest_tail -= element_width; + right_tail -= element_width; + } + if (@intFromPtr(right_tail) <= @intFromPtr(swap + 16 * element_width)) + break :outer; + } + while (compare(cmp, cmp_data, left_tail - 15 * element_width, right_tail) == GT) { + inline for (0..16) |_| { + copy(dest_tail, left_tail); + dest_tail -= element_width; + left_tail -= element_width; + } + if (@intFromPtr(left_tail) <= @intFromPtr(array + 16 * element_width)) + break :outer; + } + // Attempt to deal with the rest of the chunk in groups of 2. + var loops: usize = 8; + while (true) { + if (compare(cmp, cmp_data, left_tail, right_tail - element_width) != GT) { + inline for (0..2) |_| { + copy(dest_tail, right_tail); + dest_tail -= element_width; + right_tail -= element_width; + } + } else if (compare(cmp, cmp_data, left_tail - element_width, right_tail) == GT) { + inline for (0..2) |_| { + copy(dest_tail, left_tail); + dest_tail -= element_width; + left_tail -= element_width; + } + } else { + // Couldn't move two elements, do a cross swap and continue. + const lte = compare(cmp, cmp_data, left_tail, right_tail) != GT; + var x = if (lte) element_width else 0; + var not_x = if (!lte) element_width else 0; + dest_tail -= element_width; + copy(dest_tail + x, right_tail); + right_tail -= element_width; + copy(dest_tail + not_x, left_tail); + left_tail -= element_width; + dest_tail -= element_width; + + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + } + + loops -= 1; + if (loops == 0) + break; + } + } + + // For rest of tail, attempt to merge 2 elements a time from tail to head. + while (@intFromPtr(right_tail) > @intFromPtr(swap) + element_width and @intFromPtr(left_tail) > @intFromPtr(array) + element_width) { + // Note: I am not sure how to get the same generation as the original C. + // This implementation has an extra function call here. + // The C use `goto` to implement the two tail recursive functions below inline. + const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp_data, cmp, element_width, copy); + if (break_loop) + break; + + // Couldn't move two elements, do a cross swap and continue. + const lte = compare(cmp, cmp_data, left_tail, right_tail) != GT; + var x = if (lte) element_width else 0; + var not_x = if (!lte) element_width else 0; + dest_tail -= element_width; + copy(dest_tail + x, right_tail); + right_tail -= element_width; + copy(dest_tail + not_x, left_tail); + left_tail -= element_width; + dest_tail -= element_width; + + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + } + + // Deal with tail. + while (@intFromPtr(right_tail) >= @intFromPtr(swap) and @intFromPtr(left_tail) >= @intFromPtr(array)) { + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + } + while (@intFromPtr(right_tail) >= @intFromPtr(swap)) { + copy(dest_tail, right_tail); + dest_tail -= element_width; + right_tail -= element_width; + } +} + +// The following two functions are exactly the same but with the if blocks swapped. +// They hot loop on one side until it fails, then switch to the other list. + +fn partial_forward_merge_right_tail_2( + dest: *[*]u8, + left_head: *const [*]u8, + left_tail: *[*]u8, + right_head: *const [*]u8, + right_tail: *[*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) bool { + if (compare(cmp, cmp_data, left_tail.*, right_tail.* - element_width) != GT) { + inline for (0..2) |_| { + copy(dest.*, right_tail.*); + dest.* -= element_width; + right_tail.* -= element_width; + } + if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { + return @call(.always_tail, partial_forward_merge_right_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + if (compare(cmp, cmp_data, left_tail.* - element_width, right_tail.*) == GT) { + inline for (0..2) |_| { + copy(dest.*, left_tail.*); + dest.* -= element_width; + left_tail.* -= element_width; + } + if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { + return @call(.always_tail, partial_forward_merge_left_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + return false; +} + +fn partial_forward_merge_left_tail_2( + dest: *[*]u8, + left_head: *const [*]u8, + left_tail: *[*]u8, + right_head: *const [*]u8, + right_tail: *[*]u8, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) bool { + if (compare(cmp, cmp_data, left_tail.* - element_width, right_tail.*) == GT) { + inline for (0..2) |_| { + copy(dest.*, left_tail.*); + dest.* -= element_width; + left_tail.* -= element_width; + } + if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { + return @call(.always_tail, partial_forward_merge_left_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + if (compare(cmp, cmp_data, left_tail.*, right_tail.* - element_width) != GT) { + inline for (0..2) |_| { + copy(dest.*, right_tail.*); + dest.* -= element_width; + right_tail.* -= element_width; + } + if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { + return @call(.always_tail, partial_forward_merge_right_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + } + return true; + } + return false; +} + +/// Merges a full left block with a smaller than block size right chunk. +/// The merge goes from head to tail. fn partial_forward_merge( array: [*]u8, len: usize, @@ -98,7 +307,7 @@ fn partial_forward_merge( var right_tail = array + (len - 1) * element_width; if (compare(cmp, cmp_data, right_head - element_width, right_head) != GT) { - // Luck case, blocks happen to be sorted. + // Lucky case, blocks happen to be sorted. return; } @@ -147,9 +356,9 @@ fn partial_forward_merge( fn partial_forward_merge_right_head_2( dest: *[*]u8, left_head: *[*]u8, - left_tail: *[*]u8, + left_tail: *const [*]u8, right_head: *[*]u8, - right_tail: *[*]u8, + right_tail: *const [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, @@ -183,9 +392,9 @@ fn partial_forward_merge_right_head_2( fn partial_forward_merge_left_head_2( dest: *[*]u8, left_head: *[*]u8, - left_tail: *[*]u8, + left_tail: *const [*]u8, right_head: *[*]u8, - right_tail: *[*]u8, + right_tail: *const [*]u8, cmp_data: Opaque, cmp: CompareFn, element_width: usize, @@ -216,6 +425,80 @@ fn partial_forward_merge_left_head_2( return false; } +test "partial_backwards_merge" { + { + const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; + partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + } + + { + var expected: [64]i64 = undefined; + for (0..64) |i| { + expected[i] = @intCast(i + 1); + } + + var arr: [64]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [64]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + // chunks + for (0..16) |i| { + arr[i] = @intCast(i + 17); + } + for (0..16) |i| { + arr[i + 16] = @intCast(i + 49); + } + for (0..16) |i| { + arr[i + 32] = @intCast(i + 1); + } + for (0..16) |i| { + arr[i + 48] = @intCast(i + 33); + } + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // chunks with break + for (0..16) |i| { + arr[i] = @intCast(i + 17); + } + for (0..16) |i| { + arr[i + 32] = @intCast(i + 1); + } + for (0..16) |i| { + arr[i + 16] = @intCast(i + 49); + } + for (0..16) |i| { + arr[i + 48] = @intCast(i + 34); + } + arr[16] = 33; + arr[63] = 49; + + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + } +} + test "partial_forward_merge" { const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -329,8 +612,6 @@ fn cross_merge( if (@intFromPtr(left_tail) - @intFromPtr(left_head) > 8 * element_width) { // 8 elements all less than or equal to and can be moved together. while (compare(cmp, cmp_data, left_head + 7 * element_width, right_head) != GT) { - // TODO: Should this actually be a memcpy? - // Memcpy won't know the size until runtime but it is 1 call instead of 8. inline for (0..8) |_| { copy(dest_head, left_head); dest_head += element_width; @@ -343,8 +624,6 @@ fn cross_merge( // Attempt to do the same from the tail. // 8 elements all greater than and can be moved together. while (compare(cmp, cmp_data, left_tail - 7 * element_width, right_tail) == GT) { - // TODO: Should this actually be a memcpy? - // Memcpy won't know the size until runtime but it is 1 call instead of 8. inline for (0..8) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -359,8 +638,6 @@ fn cross_merge( if (@intFromPtr(right_tail) - @intFromPtr(right_head) > 8 * element_width) { // left greater than 8 elements right and can be moved together. while (compare(cmp, cmp_data, left_head, right_head + 7 * element_width) == GT) { - // TODO: Should this actually be a memcpy? - // Memcpy won't know the size until runtime but it is 1 call instead of 8. inline for (0..8) |_| { copy(dest_head, right_head); dest_head += element_width; @@ -373,8 +650,6 @@ fn cross_merge( // Attempt to do the same from the tail. // left less than or equalt to 8 elements right and can be moved together. while (compare(cmp, cmp_data, left_tail, right_tail - 7 * element_width) != GT) { - // TODO: Should this actually be a memcpy? - // Memcpy won't know the size until runtime but it is 1 call instead of 8. inline for (0..8) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; From 5bc97b58c830325182cea1a5901e9db31e7f89cd Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 22:43:14 -0700 Subject: [PATCH 073/203] add tail merge --- crates/compiler/builtins/bitcode/src/sort.zig | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index a8bc128d36e..6629ecb4013 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -75,6 +75,35 @@ fn quadsort_direct( // ================ Unbalanced Merges ========================================= +/// Merges the remaining blocks at the tail of the array. +fn tail_merge( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + block_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) void { + const end_ptr = array + len * element_width; + var current_block_len = block_len; + while (current_block_len < len and current_block_len <= swap_len) { + var arr_ptr = array; + while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += 2 * current_block_len * element_width) { + if (@intFromPtr(arr_ptr) + 2 * current_block_len * element_width < @intFromPtr(end_ptr)) { + partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp_data, cmp, element_width, copy); + continue; + } + const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; + partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp_data, cmp, element_width, copy); + break; + } + current_block_len *= 2; + } +} + /// Merges a full left block with a smaller than block size right chunk. /// The merge goes from tail to head. fn partial_backwards_merge( @@ -425,6 +454,29 @@ fn partial_forward_merge_left_head_2( return false; } +test "tail_merge" { + { + const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; + tail_merge(arr_ptr, 10, swap_ptr, 10, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; + tail_merge(arr_ptr, 9, swap_ptr, 9, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; + tail_merge(arr_ptr, 10, swap_ptr, 10, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + } +} + test "partial_backwards_merge" { { const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -1544,7 +1596,7 @@ test "parity_merge_two" { try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); } -test "head_merge" { +test "head_branchless_merge" { var dest = [6]i64{ 0, 0, 0, 0, 0, 0 }; var left = [4]i64{ 1, 7, 10, 22 }; var right = [4]i64{ 2, 2, 8, 22 }; @@ -1562,7 +1614,7 @@ test "head_merge" { try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); } -test "tail_merge" { +test "tail_branchless_merge" { var dest = [6]i64{ 0, 0, 0, 0, 0, 0 }; var left = [4]i64{ -22, 1, 7, 10 }; var right = [4]i64{ -22, 2, 2, 8 }; From 8726c0533982423e7dd916230d908b507c179120 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Tue, 23 Jul 2024 23:13:49 -0700 Subject: [PATCH 074/203] add quad merge --- crates/compiler/builtins/bitcode/src/sort.zig | 109 +++++++++++++++--- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 6629ecb4013..3a35fa5c480 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -73,6 +73,10 @@ fn quadsort_direct( roc_panic("todo: quadsort", 0); } +// ================ Inplace Rotate Merge ====================================== +// These are used as backup if the swap size is not large enough. +// Also used for the final merge to reduce copying of data. + // ================ Unbalanced Merges ========================================= /// Merges the remaining blocks at the tail of the array. @@ -89,7 +93,7 @@ fn tail_merge( ) void { const end_ptr = array + len * element_width; var current_block_len = block_len; - while (current_block_len < len and current_block_len <= swap_len) { + while (current_block_len < len and current_block_len <= swap_len) : (current_block_len *= 2) { var arr_ptr = array; while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += 2 * current_block_len * element_width) { if (@intFromPtr(arr_ptr) + 2 * current_block_len * element_width < @intFromPtr(end_ptr)) { @@ -100,7 +104,6 @@ fn tail_merge( partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp_data, cmp, element_width, copy); break; } - current_block_len *= 2; } } @@ -455,26 +458,24 @@ fn partial_forward_merge_left_head_2( } test "tail_merge" { - { - const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - var arr: [10]i64 = undefined; - var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - var swap: [10]i64 = undefined; - var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); - arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, expected); + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; + tail_merge(arr_ptr, 10, swap_ptr, 10, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); - arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 9, swap_ptr, 9, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, expected); + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; + tail_merge(arr_ptr, 9, swap_ptr, 9, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); - arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(arr, expected); - } + arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; + tail_merge(arr_ptr, 10, swap_ptr, 10, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); } test "partial_backwards_merge" { @@ -578,7 +579,41 @@ test "partial_forward_merge" { // ================ Quad Merge Support ======================================== -// TODO: quad_merge, requires tail merge first. +/// Merges an array of of sized blocks of sorted elements with a tail. +/// Returns the block length of sorted runs after the call. +/// This is needed if the merge ran out of swap space. +fn quad_merge( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + block_len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) usize { + const end_ptr = array + len * element_width; + var current_block_len = block_len * 4; + + while (current_block_len <= len and current_block_len <= swap_len) : (current_block_len *= 4) { + var arr_ptr = array; + while (true) { + quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp_data, cmp, element_width, copy); + + arr_ptr += current_block_len * element_width; + if (@intFromPtr(arr_ptr) + current_block_len * element_width > @intFromPtr(end_ptr)) + break; + } + + const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; + tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp_data, cmp, element_width, copy); + } + + tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp_data, cmp, element_width, copy); + + return current_block_len / 2; +} /// Merges 4 even sized blocks of sorted elements. fn quad_merge_block( @@ -743,6 +778,42 @@ fn cross_merge( } } +test "quad_merge" { + const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + var size: usize = undefined; + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + try testing.expectEqual(size, 16); + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; + size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + try testing.expectEqual(size, 16); + + arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + try testing.expectEqual(size, 8); + + // Limited swap, can't finish merge + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; + size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 1, 3, 4, 5, 6, 7, 8, 9, 2, 10 }); + try testing.expectEqual(size, 4); + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; + size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 5, 6, 7, 8, 1, 3, 4, 9, 2, 10 }); + try testing.expectEqual(size, 4); +} + test "quad_merge_block" { const expected = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; From d9d7db79e4422cfa0618d7c1cf1c97eeb47c7890 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 11:17:35 -0700 Subject: [PATCH 075/203] add core quad_swap function --- crates/compiler/builtins/bitcode/src/sort.zig | 299 +++++++++++++++++- 1 file changed, 296 insertions(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 3a35fa5c480..d5adef1fc85 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -211,6 +211,8 @@ fn partial_backwards_merge( // Note: I am not sure how to get the same generation as the original C. // This implementation has an extra function call here. // The C use `goto` to implement the two tail recursive functions below inline. + // I think the closest equivalent in zig would be to use an enum and a switch. + // That would potentially optimize to computed gotos. const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp_data, cmp, element_width, copy); if (break_loop) break; @@ -354,6 +356,8 @@ fn partial_forward_merge( // Note: I am not sure how to get the same generation as the original C. // This implementation has an extra function call here. // The C use `goto` to implement the two tail recursive functions below inline. + // I think the closest equivalent in zig would be to use an enum and a switch. + // That would potentially optimize to computed gotos. const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp_data, cmp, element_width, copy); if (break_loop) break; @@ -906,9 +910,235 @@ test "cross_merge" { // ================ 32 Element Blocks ========================================= -/// This is basically a fast path to avoid `roc_alloc` for very sort arrays. -// TODO: quad_swap, requires tail merge first. -// It deals with 32 elements without a large allocation. +const QuadSwapResult = enum { + sorted, + unfinished, +}; + +/// Starts with an unsorted array and turns it into sorted blocks of length 32. +fn quad_swap( + array: [*]u8, + len: usize, + cmp_data: Opaque, + cmp: CompareFn, + element_width: usize, + copy: CopyFn, +) QuadSwapResult { + // TODO: This is a solid amount of stack space. Is that ok? + // That said, it only ever allocates once (not recursive). + // Aside from embedded is probably ok. Just a 3 KB with 96 byte MAX_ELEMENT_BUFFER_SIZE. + var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 32]u8 align(BufferAlign) = undefined; + const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); + var tmp_buffer: BufferType align(BufferAlign) = undefined; + const tmp_ptr = @as([*]u8, @ptrCast(&tmp_buffer[0])); + + var arr_ptr = array; + var reverse_head = arr_ptr; + + // First sort groups of 8 elements. + var count = len / 8; + var skip_tail_swap = false; + outer: while (count != 0) { + count -= 1; + + var v1: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) == GT); + var v2: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) == GT); + var v3: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) == GT); + var v4: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) == GT); + + // This is an attempt at computed gotos in zig. + // Not yet sure if it will optimize as well as the raw gotos in C. + const Cases = enum { ordered, reversed, not_ordered }; + var state: Cases = switch_state: { + switch (v1 | (v2 << 1) | (v3 << 2) | (v4 << 3)) { + 0 => { + // potentially already ordered, check rest! + if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { + break :switch_state .ordered; + } + quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + + arr_ptr += 8 * element_width; + continue :outer; + }, + 15 => { + // potentially already reverse ordered, check rest! + if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + reverse_head = arr_ptr; + break :switch_state .reversed; + } + break :switch_state .not_ordered; + }, + else => { + break :switch_state .not_ordered; + }, + } + }; + while (true) { + switch (state) { + .not_ordered => { + inline for ([4]u4{ v1, v2, v3, v4 }) |v| { + const x = if (v == 0) element_width else 0; + const not_x = if (v != 0) element_width else 0; + copy(tmp_ptr, arr_ptr + x); + copy(arr_ptr, arr_ptr + not_x); + copy(arr_ptr + element_width, tmp_ptr); + arr_ptr += 2 * element_width; + } + arr_ptr -= 8 * element_width; + + quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + + arr_ptr += 8 * element_width; + continue :outer; + }, + .ordered => { + arr_ptr += 8 * element_width; + + // 1 group was order, lets see if that continues! + if (count != 0) { + count -= 1; + v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) == GT); + v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) == GT); + v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) == GT); + v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) == GT); + if (v1 | v2 | v3 | v4 != 0) { + // Sadly not ordered still, maybe reversed though? + if (v1 + v2 + v3 + v4 == 4 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + reverse_head = arr_ptr; + state = .reversed; + continue; + } + state = .not_ordered; + continue; + } + if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { + state = .ordered; + continue; + } + + quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + arr_ptr += 8 * element_width; + continue :outer; + } + break :outer; + }, + .reversed => { + arr_ptr += 8 * element_width; + + // 1 group was reversed, lets see if that continues! + if (count != 0) { + count -= 1; + v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) != GT); + v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT); + v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) != GT); + v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) != GT); + if (v1 | v2 | v3 | v4 != 0) { + // Sadly not still reversed. + // So we just need to reverse upto this point, but not the current 8 element block. + } else { + // This also checks the boundary between this and the last block. + if (compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // Row multiple reversed blocks in a row! + state = .reversed; + continue; + } + } + // Actually fix up the reversed blocks. + quad_reversal(reverse_head, arr_ptr - element_width, element_width, copy); + + // Since we already have v1 to v4, check the next block state. + if (v1 + v2 + v3 + v4 == 4 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { + state = .ordered; + continue; + } + if (v1 + v2 + v3 + v4 == 0 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + reverse_head = arr_ptr; + state = .reversed; + continue; + } + + // Just an unorderd block, do it inplace. + + inline for ([4]u4{ v1, v2, v3, v4 }) |v| { + const x = if (v == 0) element_width else 0; + const not_x = if (v != 0) element_width else 0; + copy(tmp_ptr, arr_ptr + not_x); + copy(arr_ptr, arr_ptr + x); + copy(arr_ptr + element_width, tmp_ptr); + arr_ptr += 2 * element_width; + } + arr_ptr -= 8 * element_width; + + if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + } + arr_ptr += 8; + continue :outer; + } + + // Handle tail block when reversing. + const rem = len % 8; + reverse_block: { + if (rem == 7 and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) + break :reverse_block; + if (rem >= 6 and compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) != GT) + break :reverse_block; + if (rem >= 5 and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT) + break :reverse_block; + if (rem >= 4 and compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT) + break :reverse_block; + if (rem >= 3 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT) + break :reverse_block; + if (rem >= 2 and compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) != GT) + break :reverse_block; + if (rem >= 1 and compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width) != GT) + break :reverse_block; + quad_reversal(reverse_head, arr_ptr + (rem - 1) * element_width, element_width, copy); + + // If we just reversed the entire array, it is sorted. + if (reverse_head == array) + return .sorted; + + skip_tail_swap = true; + break :outer; + } + quad_reversal(reverse_head, arr_ptr - element_width, element_width, copy); + + break :outer; + }, + } + } + } + if (!skip_tail_swap) { + tail_swap(arr_ptr, len % 8, swap, cmp_data, cmp, element_width, copy); + } + + // Group into 32 element blocks. + arr_ptr = array; + + count = len / 32; + while (count != 0) : ({ + count -= 1; + arr_ptr += 32 * element_width; + }) { + if (compare(cmp, cmp_data, arr_ptr + 7 * element_width, arr_ptr + 8 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 15 * element_width, arr_ptr + 16 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 23 * element_width, arr_ptr + 24 * element_width) != GT) { + // Already in order. + continue; + } + parity_merge(swap, arr_ptr, 8, 8, cmp_data, cmp, element_width, copy); + parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp_data, cmp, element_width, copy); + parity_merge(arr_ptr, swap, 16, 16, cmp_data, cmp, element_width, copy); + } + + // Deal with final tail for 32 element blocks. + // Anything over 8 elements is multiple blocks worth merging together. + if (len % 32 > 8) { + tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp_data, cmp, element_width, copy); + } + + return .unfinished; +} /// Merge 4 sorted arrays of length 2 into a sorted array of length 8 using swap space. fn quad_swap_merge( @@ -975,6 +1205,69 @@ fn quad_reversal( } } +test "quad_swap" { + var arr: [75]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [75]i64{ + // multiple ordered chunks + 1, 3, 5, 7, 9, 11, 13, 15, + // + 33, 34, 35, 36, 37, 38, 39, 40, + // partially ordered + 41, 42, 45, 46, 43, 44, 47, 48, + // multiple reverse chunks + 70, 69, 68, 67, 66, 65, 64, 63, + // + 16, 14, 12, 10, 8, 6, 4, 2, + // another ordered + 49, 50, 51, 52, 53, 54, 55, 56, + // unordered + 23, 21, 19, 20, 24, 22, 18, 17, + // partially reversed + 32, 31, 28, 27, 30, 29, 26, 25, + // awkward tail + 62, 59, 61, 60, 71, 73, 75, 74, + // + 72, 58, 57, + }; + + var result = quad_swap(arr_ptr, 75, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(result, .unfinished); + try testing.expectEqual(arr, [75]i64{ + // first 32 elements sorted (with 8 reversed that get flipped here) + 1, 2, 3, 4, 5, 6, 7, 8, + // + 9, 10, 11, 12, 13, 14, 15, 16, + // + 33, 34, 35, 36, 37, 38, 39, 40, + // + 41, 42, 43, 44, 45, 46, 47, 48, + // second 32 elements sorted (with 8 reversed that get flipped here) + 17, 18, 19, 20, 21, 22, 23, 24, + // + 25, 26, 27, 28, 29, 30, 31, 32, + // + 49, 50, 51, 52, 53, 54, 55, 56, + // + 63, 64, 65, 66, 67, 68, 69, 70, + // awkward tail + 57, 58, 59, 60, 61, 62, 71, 72, + // + 73, 74, 75, + }); + + // Just reversed. + var expected: [75]i64 = undefined; + for (0..75) |i| { + expected[i] = @intCast(i + 1); + arr[i] = @intCast(75 - i); + } + result = quad_swap(arr_ptr, 75, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(result, .sorted); + try testing.expectEqual(arr, expected); +} + test "quad_swap_merge" { var arr: [8]i64 = undefined; var swap: [8]i64 = undefined; From d2129aea62aee892920e4dcc6a7ddb2b9951ac73 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 11:35:15 -0700 Subject: [PATCH 076/203] full quadsort minus final merge --- crates/compiler/builtins/bitcode/src/list.zig | 2 +- crates/compiler/builtins/bitcode/src/sort.zig | 63 ++++++++++++++++--- 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index f61bb3ff19e..5f198815f87 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -710,7 +710,7 @@ pub fn listSortWith( var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec); if (list.bytes) |source_ptr| { - sort.quadsort(source_ptr, list.len(), cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy); + sort.quadsort(source_ptr, list.len(), cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); } return list; diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index d5adef1fc85..3b7698321f8 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -37,6 +37,7 @@ pub fn quadsort( data_is_owned: bool, inc_n_data: IncN, element_width: usize, + alignment: u32, copy: CopyFn, ) void { // Note, knowing constant versions of element_width and copy could have huge perf gains. @@ -46,14 +47,57 @@ pub fn quadsort( // llvm garbage collection would remove all other variants. // Also, for numeric types, inlining the compare function can be a 2x perf gain. if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { - quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy); + quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); } else { roc_panic("todo: fallback to an indirect pointer sort", 0); } } fn quadsort_direct( - source_ptr: [*]u8, + array: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + data_is_owned: bool, + inc_n_data: IncN, + element_width: usize, + alignment: u32, + copy: CopyFn, +) void { + var arr_ptr = array; + if (len < 32) { + // TODO: This is a solid amount of stack space. Is that ok? + // That said, it only ever allocates once (not recursive). + // Aside from embedded is probably ok. Just a 3 KB with 96 byte MAX_ELEMENT_BUFFER_SIZE. + // Also, zig doesn't hav alloca, so we always do max size here. + var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 32]u8 align(BufferAlign) = undefined; + const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); + tail_swap(arr_ptr, len, swap, cmp_data, cmp, element_width, copy); + } else if (quad_swap(arr_ptr, len, cmp_data, cmp, element_width, copy) != .sorted) { + var swap_size = len; + + // for crazy large arrays, limit swap. + if (len > 4194304) { + swap_size = 4194304; + while (swap_size * 8 <= len) : (swap_size *= 4) {} + } + + if (utils.alloc(swap_size * element_width, alignment)) |swap| { + const block_len = quad_merge(array, len, swap, 512, 32, cmp_data, cmp, element_width, copy); + _ = block_len; + + // TODO: final rotate merge. + + utils.dealloc(swap, alignment); + } else { + // Fallback to still sort even when out of memory. + @call(.never_inline, quadsort_stack_swap, .{ arr_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy }); + } + } +} + +fn quadsort_stack_swap( + array: [*]u8, len: usize, cmp: CompareFn, cmp_data: Opaque, @@ -64,13 +108,14 @@ fn quadsort_direct( ) void { _ = inc_n_data; _ = data_is_owned; - _ = cmp_data; - _ = len; - _ = copy; - _ = element_width; - _ = cmp; - _ = source_ptr; - roc_panic("todo: quadsort", 0); + // Use a 512 element on stack swap buffer. + var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 512]u8 align(BufferAlign) = undefined; + const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); + + const block_len = quad_merge(array, len, swap, 512, 32, cmp_data, cmp, element_width, copy); + _ = block_len; + + // TODO: final rotate merge. } // ================ Inplace Rotate Merge ====================================== From 013338f5f06e59f21ff4450195ab8a77b9cd38dc Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 11:39:23 -0700 Subject: [PATCH 077/203] remove always_tail (breaks wasm) and trust llvm to get it right (it does) --- crates/compiler/builtins/bitcode/src/sort.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 3b7698321f8..214f2d58ff0 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -308,7 +308,7 @@ fn partial_forward_merge_right_tail_2( right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return @call(.always_tail, partial_forward_merge_right_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); } return true; } @@ -319,7 +319,7 @@ fn partial_forward_merge_right_tail_2( left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return @call(.always_tail, partial_forward_merge_left_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); } return true; } @@ -344,7 +344,7 @@ fn partial_forward_merge_left_tail_2( left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return @call(.always_tail, partial_forward_merge_left_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); } return true; } @@ -355,7 +355,7 @@ fn partial_forward_merge_left_tail_2( right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return @call(.always_tail, partial_forward_merge_right_tail_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); } return true; } From 4bc168b185393aca397ea20b6e9055b90414a562 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 11:55:01 -0700 Subject: [PATCH 078/203] swap cmp and cmp_data for consistency --- crates/compiler/builtins/bitcode/src/sort.zig | 420 +++++++++--------- 1 file changed, 210 insertions(+), 210 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 214f2d58ff0..3af17c83b46 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -72,8 +72,8 @@ fn quadsort_direct( // Also, zig doesn't hav alloca, so we always do max size here. var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 32]u8 align(BufferAlign) = undefined; const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); - tail_swap(arr_ptr, len, swap, cmp_data, cmp, element_width, copy); - } else if (quad_swap(arr_ptr, len, cmp_data, cmp, element_width, copy) != .sorted) { + tail_swap(arr_ptr, len, swap, cmp, cmp_data, element_width, copy); + } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy) != .sorted) { var swap_size = len; // for crazy large arrays, limit swap. @@ -83,7 +83,7 @@ fn quadsort_direct( } if (utils.alloc(swap_size * element_width, alignment)) |swap| { - const block_len = quad_merge(array, len, swap, 512, 32, cmp_data, cmp, element_width, copy); + const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy); _ = block_len; // TODO: final rotate merge. @@ -112,7 +112,7 @@ fn quadsort_stack_swap( var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 512]u8 align(BufferAlign) = undefined; const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); - const block_len = quad_merge(array, len, swap, 512, 32, cmp_data, cmp, element_width, copy); + const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy); _ = block_len; // TODO: final rotate merge. @@ -131,8 +131,8 @@ fn tail_merge( swap: [*]u8, swap_len: usize, block_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -142,11 +142,11 @@ fn tail_merge( var arr_ptr = array; while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += 2 * current_block_len * element_width) { if (@intFromPtr(arr_ptr) + 2 * current_block_len * element_width < @intFromPtr(end_ptr)) { - partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp_data, cmp, element_width, copy); + partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy); continue; } const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; - partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp_data, cmp, element_width, copy); + partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy); break; } } @@ -160,8 +160,8 @@ fn partial_backwards_merge( swap: [*]u8, swap_len: usize, block_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -184,7 +184,7 @@ fn partial_backwards_merge( if (len <= swap_len and right_len >= 64) { // Large remaining merge and we have enough space to just do it in swap. - cross_merge(swap, array, block_len, right_len, cmp_data, cmp, element_width, copy); + cross_merge(swap, array, block_len, right_len, cmp, cmp_data, element_width, copy); @memcpy(array[0..(element_width * len)], swap[0..(element_width * len)]); @@ -242,7 +242,7 @@ fn partial_backwards_merge( left_tail -= element_width; dest_tail -= element_width; - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } loops -= 1; @@ -258,7 +258,7 @@ fn partial_backwards_merge( // The C use `goto` to implement the two tail recursive functions below inline. // I think the closest equivalent in zig would be to use an enum and a switch. // That would potentially optimize to computed gotos. - const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp_data, cmp, element_width, copy); + const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp, cmp_data, element_width, copy); if (break_loop) break; @@ -273,12 +273,12 @@ fn partial_backwards_merge( left_tail -= element_width; dest_tail -= element_width; - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } // Deal with tail. while (@intFromPtr(right_tail) >= @intFromPtr(swap) and @intFromPtr(left_tail) >= @intFromPtr(array)) { - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } while (@intFromPtr(right_tail) >= @intFromPtr(swap)) { copy(dest_tail, right_tail); @@ -296,8 +296,8 @@ fn partial_forward_merge_right_tail_2( left_tail: *[*]u8, right_head: *const [*]u8, right_tail: *[*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) bool { @@ -308,7 +308,7 @@ fn partial_forward_merge_right_tail_2( right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -319,7 +319,7 @@ fn partial_forward_merge_right_tail_2( left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -332,8 +332,8 @@ fn partial_forward_merge_left_tail_2( left_tail: *[*]u8, right_head: *const [*]u8, right_tail: *[*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) bool { @@ -344,7 +344,7 @@ fn partial_forward_merge_left_tail_2( left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -355,7 +355,7 @@ fn partial_forward_merge_left_tail_2( right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -370,8 +370,8 @@ fn partial_forward_merge( swap: [*]u8, swap_len: usize, block_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -403,7 +403,7 @@ fn partial_forward_merge( // The C use `goto` to implement the two tail recursive functions below inline. // I think the closest equivalent in zig would be to use an enum and a switch. // That would potentially optimize to computed gotos. - const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp_data, cmp, element_width, copy); + const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp, cmp_data, element_width, copy); if (break_loop) break; @@ -417,12 +417,12 @@ fn partial_forward_merge( left_head += element_width; dest_head += 2 * element_width; - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } // Deal with tail. while (@intFromPtr(left_head) <= @intFromPtr(left_tail) and @intFromPtr(right_head) <= @intFromPtr(right_tail)) { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { copy(dest_head, left_head); @@ -440,8 +440,8 @@ fn partial_forward_merge_right_head_2( left_tail: *const [*]u8, right_head: *[*]u8, right_tail: *const [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) bool { @@ -452,7 +452,7 @@ fn partial_forward_merge_right_head_2( right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); } return true; } @@ -463,7 +463,7 @@ fn partial_forward_merge_right_head_2( left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); } return true; } @@ -476,8 +476,8 @@ fn partial_forward_merge_left_head_2( left_tail: *const [*]u8, right_head: *[*]u8, right_tail: *const [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) bool { @@ -488,7 +488,7 @@ fn partial_forward_merge_left_head_2( left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); } return true; } @@ -499,7 +499,7 @@ fn partial_forward_merge_left_head_2( right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp_data, cmp, element_width, copy }); + return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); } return true; } @@ -515,15 +515,15 @@ test "tail_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 9, swap_ptr, 9, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); } @@ -537,19 +537,19 @@ test "partial_backwards_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); } @@ -577,7 +577,7 @@ test "partial_backwards_merge" { for (0..16) |i| { arr[i + 48] = @intCast(i + 33); } - partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); // chunks with break @@ -596,7 +596,7 @@ test "partial_backwards_merge" { arr[16] = 33; arr[63] = 49; - partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); } } @@ -610,19 +610,19 @@ test "partial_forward_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); } @@ -637,8 +637,8 @@ fn quad_merge( swap: [*]u8, swap_len: usize, block_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) usize { @@ -648,7 +648,7 @@ fn quad_merge( while (current_block_len <= len and current_block_len <= swap_len) : (current_block_len *= 4) { var arr_ptr = array; while (true) { - quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp_data, cmp, element_width, copy); + quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp, cmp_data, element_width, copy); arr_ptr += current_block_len * element_width; if (@intFromPtr(arr_ptr) + current_block_len * element_width > @intFromPtr(end_ptr)) @@ -656,10 +656,10 @@ fn quad_merge( } const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; - tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp_data, cmp, element_width, copy); + tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy); } - tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp_data, cmp, element_width, copy); + tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy); return current_block_len / 2; } @@ -669,8 +669,8 @@ fn quad_merge_block( array: [*]u8, swap: [*]u8, block_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -687,17 +687,17 @@ fn quad_merge_block( switch (in_order_1_2 | (in_order_3_4 << 1)) { 0 => { // Nothing sorted. Just run merges on both. - cross_merge(swap, array, block_len, block_len, cmp_data, cmp, element_width, copy); - cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp_data, cmp, element_width, copy); + cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy); }, 1 => { // First half sorted already. @memcpy(swap[0..(element_width * block_x_2)], array[0..(element_width * block_x_2)]); - cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp_data, cmp, element_width, copy); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy); }, 2 => { // Second half sorted already. - cross_merge(swap, array, block_len, block_len, cmp_data, cmp, element_width, copy); + cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy); @memcpy((swap + element_width * block_x_2)[0..(element_width * block_x_2)], block3[0..(element_width * block_x_2)]); }, 3 => { @@ -712,7 +712,7 @@ fn quad_merge_block( } // Merge 2 larger blocks. - cross_merge(array, swap, block_x_2, block_x_2, cmp_data, cmp, element_width, copy); + cross_merge(array, swap, block_x_2, block_x_2, cmp, cmp_data, element_width, copy); } /// Cross merge attempts to merge two arrays in chunks of multiple elements. @@ -721,8 +721,8 @@ fn cross_merge( src: [*]u8, left_len: usize, right_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -736,7 +736,7 @@ fn cross_merge( if (left_len + 1 >= right_len and right_len + 1 >= left_len and left_len >= 32) { const offset = 15 * element_width; if (compare(cmp, cmp_data, left_head + offset, right_head) == GT and compare(cmp, cmp_data, left_head, right_head + offset) != GT and compare(cmp, cmp_data, left_tail, right_tail - offset) == GT and compare(cmp, cmp_data, left_tail - offset, right_tail) != GT) { - parity_merge(dest, src, left_len, right_len, cmp_data, cmp, element_width, copy); + parity_merge(dest, src, left_len, right_len, cmp, cmp_data, element_width, copy); return; } } @@ -802,8 +802,8 @@ fn cross_merge( // Large enough to warrent a two way merge. var loops: usize = 8; while (true) { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); loops -= 1; if (loops == 0) @@ -813,7 +813,7 @@ fn cross_merge( // Clean up tail. while (@intFromPtr(left_head) <= @intFromPtr(left_tail) and @intFromPtr(right_head) <= @intFromPtr(right_tail)) { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { copy(dest_head, left_head); @@ -837,28 +837,28 @@ test "quad_merge" { var size: usize = undefined; arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 16); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 16); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 8); // Limited swap, can't finish merge arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [10]i64{ 1, 3, 4, 5, 6, 7, 8, 9, 2, 10 }); try testing.expectEqual(size, 4); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [10]i64{ 5, 6, 7, 8, 1, 3, 4, 9, 2, 10 }); try testing.expectEqual(size, 4); } @@ -873,27 +873,27 @@ test "quad_merge_block" { // case 0 - totally unsorted arr = [8]i64{ 7, 8, 5, 6, 3, 4, 1, 2 }; - quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); // case 1 - first half sorted arr = [8]i64{ 5, 6, 7, 8, 3, 4, 1, 2 }; - quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); // case 2 - second half sorted arr = [8]i64{ 7, 8, 5, 6, 1, 2, 3, 4 }; - quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); // case 3 both haves sorted arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; - quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); // case 3 - lucky, sorted arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; - quad_merge_block(arr_ptr, swap_ptr, 2, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); } @@ -915,7 +915,7 @@ test "cross_merge" { for (0..32) |i| { src[i + 32] = @intCast(i + 1); } - cross_merge(dest_ptr, src_ptr, 32, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, expected); // will fallback, every other @@ -923,7 +923,7 @@ test "cross_merge" { src[i * 2] = @intCast(i * 2 + 1); src[i * 2 + 1] = @intCast(i * 2 + 2); } - cross_merge(dest_ptr, src_ptr, 32, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, expected); // super uneven @@ -933,7 +933,7 @@ test "cross_merge" { for (0..44) |i| { src[i + 20] = @intCast(i + 1); } - cross_merge(dest_ptr, src_ptr, 20, 44, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 20, 44, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, expected); // chunks @@ -949,7 +949,7 @@ test "cross_merge" { for (0..16) |i| { src[i + 48] = @intCast(i + 33); } - cross_merge(dest_ptr, src_ptr, 32, 32, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, expected); } @@ -964,8 +964,8 @@ const QuadSwapResult = enum { fn quad_swap( array: [*]u8, len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) QuadSwapResult { @@ -1001,7 +1001,7 @@ fn quad_swap( if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { break :switch_state .ordered; } - quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); arr_ptr += 8 * element_width; continue :outer; @@ -1032,7 +1032,7 @@ fn quad_swap( } arr_ptr -= 8 * element_width; - quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); arr_ptr += 8 * element_width; continue :outer; @@ -1062,7 +1062,7 @@ fn quad_swap( continue; } - quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); arr_ptr += 8 * element_width; continue :outer; } @@ -1116,7 +1116,7 @@ fn quad_swap( arr_ptr -= 8 * element_width; if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { - quad_swap_merge(arr_ptr, swap, cmp_data, cmp, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); } arr_ptr += 8; continue :outer; @@ -1156,7 +1156,7 @@ fn quad_swap( } } if (!skip_tail_swap) { - tail_swap(arr_ptr, len % 8, swap, cmp_data, cmp, element_width, copy); + tail_swap(arr_ptr, len % 8, swap, cmp, cmp_data, element_width, copy); } // Group into 32 element blocks. @@ -1171,15 +1171,15 @@ fn quad_swap( // Already in order. continue; } - parity_merge(swap, arr_ptr, 8, 8, cmp_data, cmp, element_width, copy); - parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp_data, cmp, element_width, copy); - parity_merge(arr_ptr, swap, 16, 16, cmp_data, cmp, element_width, copy); + parity_merge(swap, arr_ptr, 8, 8, cmp, cmp_data, element_width, copy); + parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp, cmp_data, element_width, copy); + parity_merge(arr_ptr, swap, 16, 16, cmp, cmp_data, element_width, copy); } // Deal with final tail for 32 element blocks. // Anything over 8 elements is multiple blocks worth merging together. if (len % 32 > 8) { - tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp_data, cmp, element_width, copy); + tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp, cmp_data, element_width, copy); } return .unfinished; @@ -1189,15 +1189,15 @@ fn quad_swap( fn quad_swap_merge( array: [*]u8, swap: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { - parity_merge_two(swap, array, cmp_data, cmp, element_width, copy); - parity_merge_two(swap + 4 * element_width, array + 4 * element_width, cmp_data, cmp, element_width, copy); + parity_merge_two(swap, array, cmp, cmp_data, element_width, copy); + parity_merge_two(swap + 4 * element_width, array + 4 * element_width, cmp, cmp_data, element_width, copy); - parity_merge_four(array, swap, cmp_data, cmp, element_width, copy); + parity_merge_four(array, swap, cmp, cmp_data, element_width, copy); } /// Reverse values from start to end. @@ -1277,7 +1277,7 @@ test "quad_swap" { 72, 58, 57, }; - var result = quad_swap(arr_ptr, 75, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + var result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(result, .unfinished); try testing.expectEqual(arr, [75]i64{ // first 32 elements sorted (with 8 reversed that get flipped here) @@ -1308,7 +1308,7 @@ test "quad_swap" { expected[i] = @intCast(i + 1); arr[i] = @intCast(75 - i); } - result = quad_swap(arr_ptr, 75, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(result, .sorted); try testing.expectEqual(arr, expected); } @@ -1321,17 +1321,17 @@ test "quad_swap_merge" { arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - quad_swap_merge(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 7, 1, 3, 6, 8, 2, 4 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - quad_swap_merge(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 8, 3, 4, 5, 6, 2, 7 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - quad_swap_merge(arr_ptr, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } @@ -1361,15 +1361,15 @@ fn tail_swap( array: [*]u8, len: usize, swap: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { std.debug.assert(len < 32); if (len < 8) { - tiny_sort(array, len, swap, cmp_data, cmp, element_width, copy); + tiny_sort(array, len, swap, cmp, cmp_data, element_width, copy); return; } @@ -1381,21 +1381,21 @@ fn tail_swap( const quad4 = half2 - quad3; var arr_ptr = array; - tail_swap(arr_ptr, quad1, swap, cmp_data, cmp, element_width, copy); + tail_swap(arr_ptr, quad1, swap, cmp, cmp_data, element_width, copy); arr_ptr += quad1 * element_width; - tail_swap(arr_ptr, quad2, swap, cmp_data, cmp, element_width, copy); + tail_swap(arr_ptr, quad2, swap, cmp, cmp_data, element_width, copy); arr_ptr += quad2 * element_width; - tail_swap(arr_ptr, quad3, swap, cmp_data, cmp, element_width, copy); + tail_swap(arr_ptr, quad3, swap, cmp, cmp_data, element_width, copy); arr_ptr += quad3 * element_width; - tail_swap(arr_ptr, quad4, swap, cmp_data, cmp, element_width, copy); + tail_swap(arr_ptr, quad4, swap, cmp, cmp_data, element_width, copy); if (compare(cmp, cmp_data, array + (quad1 - 1) * element_width, array + quad1 * element_width) != GT and compare(cmp, cmp_data, array + (half1 - 1) * element_width, array + half1 * element_width) != GT and compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr) != GT) { return; } - parity_merge(swap, array, quad1, quad2, cmp_data, cmp, element_width, copy); - parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp_data, cmp, element_width, copy); - parity_merge(array, swap, half1, half2, cmp_data, cmp, element_width, copy); + parity_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy); + parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy); + parity_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy); } /// Merges two neighboring sorted arrays into dest. @@ -1405,8 +1405,8 @@ fn parity_merge( src: [*]u8, left_len: usize, right_len: usize, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -1421,16 +1421,16 @@ fn parity_merge( var dest_tail = dest + (left_len + right_len - 1) * element_width; if (left_len < right_len) { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); var ll = left_len - 1; while (ll != 0) : (ll -= 1) { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } test "tail_swap" { @@ -1449,7 +1449,7 @@ test "tail_swap" { var rng = std.rand.DefaultPrng.init(seed); rng.random().shuffle(i64, arr[0..]); - tail_swap(arr_ptr, 31, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_swap(arr_ptr, 31, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, expected); } } @@ -1464,12 +1464,12 @@ test "parity_merge" { arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 4, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } { @@ -1481,12 +1481,12 @@ test "parity_merge" { arr = [9]i64{ 1, 3, 5, 8, 2, 4, 6, 7, 9 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 5, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); arr = [9]i64{ 6, 7, 8, 9, 1, 2, 3, 4, 5 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 5, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); } } @@ -1499,8 +1499,8 @@ fn tiny_sort( array: [*]u8, len: usize, swap: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -1514,27 +1514,27 @@ fn tiny_sort( return; }, 2 => { - swap_branchless(array, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(array, tmp_ptr, cmp, cmp_data, element_width, copy); }, 3 => { var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); }, 4 => { - parity_swap_four(array, tmp_ptr, cmp_data, cmp, element_width, copy); + parity_swap_four(array, tmp_ptr, cmp, cmp_data, element_width, copy); }, 5 => { - parity_swap_five(array, tmp_ptr, cmp_data, cmp, element_width, copy); + parity_swap_five(array, tmp_ptr, cmp, cmp_data, element_width, copy); }, 6 => { - parity_swap_six(array, tmp_ptr, swap, cmp_data, cmp, element_width, copy); + parity_swap_six(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy); }, 7 => { - parity_swap_seven(array, tmp_ptr, swap, cmp_data, cmp, element_width, copy); + parity_swap_seven(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy); }, else => { unreachable; @@ -1545,15 +1545,15 @@ fn tiny_sort( fn parity_swap_four( array: [*]u8, tmp_ptr: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; @@ -1562,44 +1562,44 @@ fn parity_swap_four( copy(arr_ptr, arr_ptr + element_width); copy(arr_ptr + element_width, tmp_ptr); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); } } fn parity_swap_five( array: [*]u8, tmp_ptr: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; - var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr = array; if (more_work != 0) { - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); } } @@ -1607,27 +1607,27 @@ fn parity_swap_six( array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 3 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr = array; { const lte = compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT; if (lte) { - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 4 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); return; } } @@ -1654,16 +1654,16 @@ fn parity_swap_six( var left = swap; var right = swap + 3 * element_width; - head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); arr_ptr = array + 5 * element_width; left = swap + 2 * element_width; right = swap + 5 * element_width; - tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; const from = if (gt) left else right; copy(arr_ptr, from); @@ -1673,29 +1673,29 @@ fn parity_swap_seven( array: [*]u8, tmp_ptr: [*]u8, swap: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= 3 * element_width; - var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; - more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr -= element_width; if (more_work == 0) return; - swap_branchless(arr_ptr, tmp_ptr, cmp_data, cmp, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr = array; { @@ -1727,17 +1727,17 @@ fn parity_swap_seven( var left = swap; var right = swap + 3 * element_width; - head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); arr_ptr = array + 6 * element_width; left = swap + 2 * element_width; right = swap + 6 * element_width; - tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&arr_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; const from = if (gt) left else right; copy(arr_ptr, from); @@ -1752,11 +1752,11 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [7]i64{ 3, 1, 2, 5, 4, 7, 6 }; - tiny_sort(arr_ptr, 7, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); arr = [7]i64{ 7, 6, 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 7, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); } { @@ -1764,11 +1764,11 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [6]i64{ 3, 1, 2, 6, 4, 5 }; - tiny_sort(arr_ptr, 6, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); arr = [6]i64{ 6, 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 6, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); } { @@ -1776,11 +1776,11 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [5]i64{ 2, 1, 4, 3, 5 }; - tiny_sort(arr_ptr, 5, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); arr = [5]i64{ 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 5, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); } { @@ -1788,23 +1788,23 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [4]i64{ 4, 2, 1, 3 }; - tiny_sort(arr_ptr, 4, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 1, 4, 3 }; - tiny_sort(arr_ptr, 4, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); } { var arr = [3]i64{ 2, 3, 1 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - tiny_sort(arr_ptr, 3, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 3, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [3]i64{ 1, 2, 3 }); } { var arr = [2]i64{ 2, 1 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - tiny_sort(arr_ptr, 2, swap_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 2, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ 1, 2 }); } } @@ -1818,17 +1818,17 @@ test "tiny_sort" { inline fn parity_merge_four( dest: [*]u8, array: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { var left = array; var right = array + (4 * element_width); var dest_ptr = dest; - head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); - head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; copy(dest_ptr, to_copy); @@ -1836,9 +1836,9 @@ inline fn parity_merge_four( left = array + (3 * element_width); right = array + (7 * element_width); dest_ptr = dest + (7 * element_width); - tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); - tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; to_copy = if (gt) left else right; copy(dest_ptr, to_copy); @@ -1848,15 +1848,15 @@ inline fn parity_merge_four( inline fn parity_merge_two( dest: [*]u8, array: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { var left = array; var right = array + (2 * element_width); var dest_ptr = dest; - head_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); const lte = compare(cmp, cmp_data, left, right) != GT; var to_copy = if (lte) left else right; copy(dest_ptr, to_copy); @@ -1864,7 +1864,7 @@ inline fn parity_merge_two( left = array + element_width; right = array + (3 * element_width); dest_ptr = dest + (3 * element_width); - tail_branchless_merge(&dest_ptr, &left, &right, cmp_data, cmp, element_width, copy); + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); const gt = compare(cmp, cmp_data, left, right) == GT; to_copy = if (gt) left else right; copy(dest_ptr, to_copy); @@ -1878,8 +1878,8 @@ inline fn head_branchless_merge( dest: *[*]u8, left: *[*]u8, right: *[*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -1901,8 +1901,8 @@ inline fn tail_branchless_merge( dest: *[*]u8, left: *[*]u8, right: *[*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { @@ -1920,20 +1920,20 @@ inline fn tail_branchless_merge( inline fn swap_branchless( ptr: [*]u8, tmp: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) void { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - _ = swap_branchless_return_gt(ptr, tmp, cmp_data, cmp, element_width, copy); + _ = swap_branchless_return_gt(ptr, tmp, cmp, cmp_data, element_width, copy); } inline fn swap_branchless_return_gt( ptr: [*]u8, tmp: [*]u8, - cmp_data: Opaque, cmp: CompareFn, + cmp_data: Opaque, element_width: usize, copy: CopyFn, ) u8 { @@ -1959,17 +1959,17 @@ test "parity_merge_four" { arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } @@ -1981,27 +1981,27 @@ test "parity_merge_two" { arr = [4]i64{ 1, 2, 3, 4 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 3, 2, 4 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 3, 4, 1, 2 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 4, 1, 3 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 4, 2, 3 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); } @@ -2013,12 +2013,12 @@ test "head_branchless_merge" { var left_ptr = @as([*]u8, @ptrCast(&left[0])); var right_ptr = @as([*]u8, @ptrCast(&right[0])); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); } @@ -2031,12 +2031,12 @@ test "tail_branchless_merge" { var left_ptr = @as([*]u8, @ptrCast(&left[left.len - 1])); var right_ptr = @as([*]u8, @ptrCast(&right[right.len - 1])); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); } @@ -2048,15 +2048,15 @@ test "swap" { var tmp_ptr = @as([*]u8, @ptrCast(&tmp)); arr = [2]i64{ 10, 20 }; - swap_branchless(arr_ptr, tmp_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ 10, 20 }); arr = [2]i64{ 77, -12 }; - swap_branchless(arr_ptr, tmp_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ -12, 77 }); arr = [2]i64{ -22, -22 }; - swap_branchless(arr_ptr, tmp_ptr, null, &test_i64_compare, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); try testing.expectEqual(arr, [2]i64{ -22, -22 }); } From eef5b1a34982b2271a23b4621b599720f9daf1de Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 13:02:51 -0700 Subject: [PATCH 079/203] correctly pass in swap size --- crates/compiler/builtins/bitcode/src/sort.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 3af17c83b46..c25d4f80214 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -83,7 +83,7 @@ fn quadsort_direct( } if (utils.alloc(swap_size * element_width, alignment)) |swap| { - const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy); + const block_len = quad_merge(arr_ptr, len, swap, swap_size, 32, cmp, cmp_data, element_width, copy); _ = block_len; // TODO: final rotate merge. From 2bbb17c1de1958c8faf635353fca5b454ef2298f Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 14:06:07 -0700 Subject: [PATCH 080/203] improve zig panic clarity --- crates/compiler/builtins/bitcode/src/main.zig | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/main.zig b/crates/compiler/builtins/bitcode/src/main.zig index d4ffa023575..6b677519b9c 100644 --- a/crates/compiler/builtins/bitcode/src/main.zig +++ b/crates/compiler/builtins/bitcode/src/main.zig @@ -394,11 +394,13 @@ fn exportUtilsFn(comptime func: anytype, comptime func_name: []const u8) void { // Custom panic function, as builtin Zig version errors during LLVM verification pub fn panic(message: []const u8, stacktrace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - if (builtin.is_test) { - std.debug.print("{s}: {?}", .{ message, stacktrace }); + if (builtin.target.cpu.arch != .wasm32) { + std.debug.print("\nSomehow in unreachable zig panic!\nThis is a roc standard libarry bug\n{s}: {?}", .{ message, stacktrace }); + std.process.abort(); + } else { + // Can't call abort or print from wasm. Just leave it as unreachable. + unreachable; } - - unreachable; } // Run all tests in imported modules From 1e7a03ef12ba635d87fb502956f924da34d7a9b6 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 20:26:26 -0700 Subject: [PATCH 081/203] block broken case for now --- crates/compiler/builtins/bitcode/src/sort.zig | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index c25d4f80214..7fe3c101db2 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -76,11 +76,11 @@ fn quadsort_direct( } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy) != .sorted) { var swap_size = len; - // for crazy large arrays, limit swap. - if (len > 4194304) { - swap_size = 4194304; - while (swap_size * 8 <= len) : (swap_size *= 4) {} - } + // This is optional, for about 5% perf hit, lower memory usage on large arrays. + // if (len > 4194304) { + // swap_size = 4194304; + // while (swap_size * 8 <= len) : (swap_size *= 4) {} + // } if (utils.alloc(swap_size * element_width, alignment)) |swap| { const block_len = quad_merge(arr_ptr, len, swap, swap_size, 32, cmp, cmp_data, element_width, copy); @@ -1006,14 +1006,15 @@ fn quad_swap( arr_ptr += 8 * element_width; continue :outer; }, - 15 => { - // potentially already reverse ordered, check rest! - if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { - reverse_head = arr_ptr; - break :switch_state .reversed; - } - break :switch_state .not_ordered; - }, + // TODO: Figure out why the reversed case below is broken in some cases. + // 15 => { + // // potentially already reverse ordered, check rest! + // if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // reverse_head = arr_ptr; + // break :switch_state .reversed; + // } + // break :switch_state .not_ordered; + // }, else => { break :switch_state .not_ordered; }, From eba13033af0ed0fddc72ef51724e4555d337ddbb Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 20:38:00 -0700 Subject: [PATCH 082/203] add extra note --- crates/compiler/builtins/bitcode/src/sort.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 7fe3c101db2..c29f1c6ff3c 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1007,6 +1007,7 @@ fn quad_swap( continue :outer; }, // TODO: Figure out why the reversed case below is broken in some cases. + // Note, it seems like the reverse itself is not broken, but the count and pointer become off somehow. // 15 => { // // potentially already reverse ordered, check rest! // if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { From 92330394a9c393ff63b3452eab4dbb74b8b3cf6f Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 21:25:00 -0700 Subject: [PATCH 083/203] disable broken test for now --- crates/compiler/builtins/bitcode/src/sort.zig | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index c29f1c6ff3c..cbfe280c20d 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1252,68 +1252,68 @@ fn quad_reversal( } } -test "quad_swap" { - var arr: [75]i64 = undefined; - var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - - arr = [75]i64{ - // multiple ordered chunks - 1, 3, 5, 7, 9, 11, 13, 15, - // - 33, 34, 35, 36, 37, 38, 39, 40, - // partially ordered - 41, 42, 45, 46, 43, 44, 47, 48, - // multiple reverse chunks - 70, 69, 68, 67, 66, 65, 64, 63, - // - 16, 14, 12, 10, 8, 6, 4, 2, - // another ordered - 49, 50, 51, 52, 53, 54, 55, 56, - // unordered - 23, 21, 19, 20, 24, 22, 18, 17, - // partially reversed - 32, 31, 28, 27, 30, 29, 26, 25, - // awkward tail - 62, 59, 61, 60, 71, 73, 75, 74, - // - 72, 58, 57, - }; - - var result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(result, .unfinished); - try testing.expectEqual(arr, [75]i64{ - // first 32 elements sorted (with 8 reversed that get flipped here) - 1, 2, 3, 4, 5, 6, 7, 8, - // - 9, 10, 11, 12, 13, 14, 15, 16, - // - 33, 34, 35, 36, 37, 38, 39, 40, - // - 41, 42, 43, 44, 45, 46, 47, 48, - // second 32 elements sorted (with 8 reversed that get flipped here) - 17, 18, 19, 20, 21, 22, 23, 24, - // - 25, 26, 27, 28, 29, 30, 31, 32, - // - 49, 50, 51, 52, 53, 54, 55, 56, - // - 63, 64, 65, 66, 67, 68, 69, 70, - // awkward tail - 57, 58, 59, 60, 61, 62, 71, 72, - // - 73, 74, 75, - }); - - // Just reversed. - var expected: [75]i64 = undefined; - for (0..75) |i| { - expected[i] = @intCast(i + 1); - arr[i] = @intCast(75 - i); - } - result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - try testing.expectEqual(result, .sorted); - try testing.expectEqual(arr, expected); -} +// test "quad_swap" { +// var arr: [75]i64 = undefined; +// var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + +// arr = [75]i64{ +// // multiple ordered chunks +// 1, 3, 5, 7, 9, 11, 13, 15, +// // +// 33, 34, 35, 36, 37, 38, 39, 40, +// // partially ordered +// 41, 42, 45, 46, 43, 44, 47, 48, +// // multiple reverse chunks +// 70, 69, 68, 67, 66, 65, 64, 63, +// // +// 16, 14, 12, 10, 8, 6, 4, 2, +// // another ordered +// 49, 50, 51, 52, 53, 54, 55, 56, +// // unordered +// 23, 21, 19, 20, 24, 22, 18, 17, +// // partially reversed +// 32, 31, 28, 27, 30, 29, 26, 25, +// // awkward tail +// 62, 59, 61, 60, 71, 73, 75, 74, +// // +// 72, 58, 57, +// }; + +// var result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); +// try testing.expectEqual(result, .unfinished); +// try testing.expectEqual(arr, [75]i64{ +// // first 32 elements sorted (with 8 reversed that get flipped here) +// 1, 2, 3, 4, 5, 6, 7, 8, +// // +// 9, 10, 11, 12, 13, 14, 15, 16, +// // +// 33, 34, 35, 36, 37, 38, 39, 40, +// // +// 41, 42, 43, 44, 45, 46, 47, 48, +// // second 32 elements sorted (with 8 reversed that get flipped here) +// 17, 18, 19, 20, 21, 22, 23, 24, +// // +// 25, 26, 27, 28, 29, 30, 31, 32, +// // +// 49, 50, 51, 52, 53, 54, 55, 56, +// // +// 63, 64, 65, 66, 67, 68, 69, 70, +// // awkward tail +// 57, 58, 59, 60, 61, 62, 71, 72, +// // +// 73, 74, 75, +// }); + +// // Just reversed. +// var expected: [75]i64 = undefined; +// for (0..75) |i| { +// expected[i] = @intCast(i + 1); +// arr[i] = @intCast(75 - i); +// } +// result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); +// try testing.expectEqual(result, .sorted); +// try testing.expectEqual(arr, expected); +// } test "quad_swap_merge" { var arr: [8]i64 = undefined; From b8f488edcfcef8e78dab6fdc939119ffd01ed370 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 21:25:10 -0700 Subject: [PATCH 084/203] add trinity merge --- crates/compiler/builtins/bitcode/src/sort.zig | 211 +++++++++++++++++- 1 file changed, 210 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index cbfe280c20d..b80f076c0d9 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -120,7 +120,216 @@ fn quadsort_stack_swap( // ================ Inplace Rotate Merge ====================================== // These are used as backup if the swap size is not large enough. -// Also used for the final merge to reduce copying of data. +// Also can be used for the final merge to reduce memory footprint. + +fn trinity_rotation( + array: [*]u8, + len: usize, + swap: [*]u8, + full_swap_len: usize, + left_len: usize, + element_width: usize, + copy: CopyFn, +) void { + var buffer: BufferType align(BufferAlign) = undefined; + const tmp_ptr = @as([*]u8, @ptrCast(&buffer[0])); + + const right_len = len - left_len; + + var swap_len = full_swap_len; + if (full_swap_len > 65536) { + swap_len = 65536; + } + + if (left_len < right_len) { + if (left_len <= swap_len) { + @memcpy(swap[0..(element_width * left_len)], array[0..(element_width * left_len)]); + std.mem.copyForwards(u8, array[0..(element_width * right_len)], (array + left_len * element_width)[0..(element_width * right_len)]); + @memcpy((array + right_len * element_width)[0..(element_width * left_len)], swap[0..(element_width * left_len)]); + } else { + var a_ptr = array; + var b_ptr = a_ptr + left_len * element_width; + + var bridge = right_len - left_len; + if (bridge <= swap_len and bridge > 3) { + var c_ptr = a_ptr + right_len * element_width; + var d_ptr = c_ptr + left_len * element_width; + + @memcpy(swap[0..(bridge * element_width)], b_ptr[0..(bridge * element_width)]); + + for (0..left_len) |_| { + c_ptr -= element_width; + d_ptr -= element_width; + copy(c_ptr, d_ptr); + b_ptr -= element_width; + copy(d_ptr, b_ptr); + } + @memcpy(a_ptr[0..(bridge * element_width)], swap[0..(bridge * element_width)]); + } else { + var c_ptr = b_ptr; + var d_ptr = c_ptr + right_len * element_width; + + bridge = left_len / 2; + + for (0..bridge) |_| { + b_ptr -= element_width; + copy(tmp_ptr, b_ptr); + copy(b_ptr, a_ptr); + copy(a_ptr, c_ptr); + a_ptr += element_width; + d_ptr -= element_width; + copy(c_ptr, d_ptr); + c_ptr += element_width; + copy(d_ptr, tmp_ptr); + } + + bridge = (@intFromPtr(d_ptr) - @intFromPtr(c_ptr)) / (element_width * 2); + for (0..bridge) |_| { + copy(tmp_ptr, c_ptr); + d_ptr -= element_width; + copy(c_ptr, d_ptr); + c_ptr += element_width; + copy(d_ptr, a_ptr); + copy(a_ptr, tmp_ptr); + a_ptr += element_width; + } + + bridge = (@intFromPtr(d_ptr) - @intFromPtr(a_ptr)) / (element_width * 2); + for (0..bridge) |_| { + copy(tmp_ptr, a_ptr); + d_ptr -= element_width; + copy(a_ptr, d_ptr); + a_ptr += element_width; + copy(d_ptr, tmp_ptr); + } + } + } + } else if (right_len < left_len) { + if (right_len <= swap_len) { + @memcpy(swap[0..(element_width * right_len)], (array + left_len * element_width)[0..(element_width * right_len)]); + std.mem.copyBackwards(u8, (array + right_len * element_width)[0..(element_width * left_len)], array[0..(element_width * left_len)]); + @memcpy(array[0..(element_width * right_len)], swap[0..(element_width * right_len)]); + } else { + var a_ptr = array; + var b_ptr = a_ptr + left_len * element_width; + + var bridge = left_len - right_len; + if (bridge <= swap_len and bridge > 3) { + var c_ptr = a_ptr + right_len * element_width; + var d_ptr = c_ptr + left_len * element_width; + + @memcpy(swap[0..(bridge * element_width)], c_ptr[0..(bridge * element_width)]); + + for (0..right_len) |_| { + copy(c_ptr, a_ptr); + c_ptr += element_width; + copy(a_ptr, b_ptr); + a_ptr += element_width; + b_ptr += element_width; + } + @memcpy((d_ptr - bridge * element_width)[0..(bridge * element_width)], swap[0..(bridge * element_width)]); + } else { + var c_ptr = b_ptr; + var d_ptr = c_ptr + right_len * element_width; + + bridge = right_len / 2; + + for (0..bridge) |_| { + b_ptr -= element_width; + copy(tmp_ptr, b_ptr); + copy(b_ptr, a_ptr); + copy(a_ptr, c_ptr); + a_ptr += element_width; + d_ptr -= element_width; + copy(c_ptr, d_ptr); + c_ptr += element_width; + copy(d_ptr, tmp_ptr); + } + + bridge = (@intFromPtr(b_ptr) - @intFromPtr(a_ptr)) / (element_width * 2); + for (0..bridge) |_| { + b_ptr -= element_width; + copy(tmp_ptr, b_ptr); + copy(b_ptr, a_ptr); + d_ptr -= element_width; + copy(a_ptr, d_ptr); + a_ptr += element_width; + copy(d_ptr, tmp_ptr); + } + + bridge = (@intFromPtr(d_ptr) - @intFromPtr(a_ptr)) / (element_width * 2); + for (0..bridge) |_| { + copy(tmp_ptr, a_ptr); + d_ptr -= element_width; + copy(a_ptr, d_ptr); + a_ptr += element_width; + copy(d_ptr, tmp_ptr); + } + } + } + } else { + var left_ptr = array; + var right_ptr = left_ptr + left_len * element_width; + + for (0..left_len) |_| { + copy(tmp_ptr, left_ptr); + copy(left_ptr, right_ptr); + left_ptr += element_width; + copy(right_ptr, tmp_ptr); + right_ptr += element_width; + } + } +} + +test "trinity_rotation" { + { + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + // Even. + arr = [10]i64{ 6, 7, 8, 9, 10, 1, 2, 3, 4, 5 }; + trinity_rotation(arr_ptr, 10, swap_ptr, 10, 5, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + + // left large, right fits in swap. + arr = [10]i64{ 3, 4, 5, 6, 7, 8, 9, 10, 1, 2 }; + trinity_rotation(arr_ptr, 10, swap_ptr, 10, 8, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + + // right large, left fits in swap. + arr = [10]i64{ 9, 10, 1, 2, 3, 4, 5, 6, 7, 8 }; + trinity_rotation(arr_ptr, 10, swap_ptr, 10, 2, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + + // left large, no swap. + arr = [10]i64{ 3, 4, 5, 6, 7, 8, 9, 10, 1, 2 }; + trinity_rotation(arr_ptr, 10, swap_ptr, 0, 8, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + + // right large, no swap. + arr = [10]i64{ 9, 10, 1, 2, 3, 4, 5, 6, 7, 8 }; + trinity_rotation(arr_ptr, 10, swap_ptr, 0, 2, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); + } + { + var arr: [16]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [5]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + // left larger, bridge in swap. + arr = [16]i64{ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6 }; + trinity_rotation(arr_ptr, 16, swap_ptr, 5, 10, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [16]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }); + + // // right large, bridge in swap. + arr = [16]i64{ 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + trinity_rotation(arr_ptr, 16, swap_ptr, 5, 6, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, [16]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }); + } +} // ================ Unbalanced Merges ========================================= From 9e7619ef2a4774f4d5eace09e09ca694f3b92f8c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 21:39:30 -0700 Subject: [PATCH 085/203] add monobound binary search --- crates/compiler/builtins/bitcode/src/sort.zig | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index b80f076c0d9..7e2f749b552 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -122,6 +122,34 @@ fn quadsort_stack_swap( // These are used as backup if the swap size is not large enough. // Also can be used for the final merge to reduce memory footprint. +/// Binary search, but more cache friendly! +fn monobound_binary_first( + array: [*]u8, + initial_top: usize, + value_ptr: [*]u8, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, +) usize { + var top = initial_top; + var end_ptr = array + top * element_width; + + while (top > 1) { + const mid = top / 2; + + if (compare(cmp, cmp_data, value_ptr, end_ptr - mid * element_width) != GT) { + end_ptr -= mid * element_width; + } + top -= mid; + } + + if (compare(cmp, cmp_data, value_ptr, end_ptr - element_width) != GT) { + end_ptr -= element_width; + } + return (@intFromPtr(end_ptr) - @intFromPtr(array)) / element_width; +} + +/// Swap two neighboring chunks of an array quickly with limited memory. fn trinity_rotation( array: [*]u8, len: usize, @@ -281,6 +309,33 @@ fn trinity_rotation( } } +test "monobound_binary_first" { + var arr = [25]i64{ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49 }; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var value: i64 = undefined; + var value_ptr = @as([*]u8, @ptrCast(&value)); + + value = 7; + var res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + try testing.expectEqual(res, 3); + + value = 39; + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + try testing.expectEqual(res, 19); + + value = 40; + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + try testing.expectEqual(res, 20); + + value = -10; + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + try testing.expectEqual(res, 0); + + value = 10000; + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + try testing.expectEqual(res, 25); +} + test "trinity_rotation" { { var arr: [10]i64 = undefined; From 0baacb3c00f7b4d46f3ef2802b20141125d87897 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 22:09:23 -0700 Subject: [PATCH 086/203] add rotate merge block --- crates/compiler/builtins/bitcode/src/sort.zig | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 7e2f749b552..9892218abad 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -122,6 +122,64 @@ fn quadsort_stack_swap( // These are used as backup if the swap size is not large enough. // Also can be used for the final merge to reduce memory footprint. +/// Merges two blocks together while only using limited memory. +fn rotate_merge_block( + array: [*]u8, + swap: [*]u8, + swap_len: usize, + initial_left_block: usize, + initial_right: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, +) void { + var left_block = initial_left_block; + var right = initial_right; + if (compare(cmp, cmp_data, array + (left_block - 1) * element_width, array + left_block * element_width) != GT) { + // Lucky us, already sorted. + return; + } + + var right_block = left_block / 2; + left_block -= right_block; + + var left = monobound_binary_first(array + (left_block + right_block) * element_width, right, array + left_block * element_width, cmp, cmp_data, element_width); + right -= left; + + if (left != 0) { + if (left_block + left <= swap_len) { + @memcpy(swap[0 .. left_block * element_width], array[0 .. left_block * element_width]); + @memcpy((swap + left_block * element_width[0 .. left * element_width]), (array + (left_block + right_block) * element_width)[0 .. left * element_width]); + std.mem.copyBackwards(u8, (array + (left + left_block) * element_width)[0..(right_block * element_width)], (array + left_block * element_width)[0..(right_block * element_width)]); + + cross_merge(array, swap, left_block, left, cmp, cmp_data, element_width, copy); + } else { + trinity_rotation(array + left_block * element_width, right_block + left, swap, swap_len, right_block, element_width, copy); + + const unbalanced = (left * 2 < left_block) | (left_block * 2 < left); + if (unbalanced and left <= swap_len) { + partial_backwards_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy); + } else if (unbalanced and left_block <= swap_len) { + partial_forward_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy); + } else { + rotate_merge_block(array, swap, swap_len, left_block, left, cmp, cmp_data, element_width, copy); + } + } + } + + if (right != 0) { + const unbalanced = (right * 2 < right_block) | (right_block * 2 < right); + if ((unbalanced and right <= swap_len) or right + right_block <= swap_len) { + partial_backwards_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy); + } else if (unbalanced and left_block <= swap_len) { + partial_forward_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy); + } else { + rotate_merge_block(array + (left_block + left) * element_width, swap, swap_len, right_block, right, cmp, cmp_data, element_width, copy); + } + } +} + /// Binary search, but more cache friendly! fn monobound_binary_first( array: [*]u8, From 3093e79269d62cd7c4c7ab7ceacca3d810295935 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 22:43:06 -0700 Subject: [PATCH 087/203] add final rotate merge --- crates/compiler/builtins/bitcode/src/sort.zig | 89 ++++++++++++++++--- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 9892218abad..4d5deb51ccd 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -74,19 +74,18 @@ fn quadsort_direct( const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); tail_swap(arr_ptr, len, swap, cmp, cmp_data, element_width, copy); } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy) != .sorted) { - var swap_size = len; + var swap_len = len; // This is optional, for about 5% perf hit, lower memory usage on large arrays. // if (len > 4194304) { - // swap_size = 4194304; - // while (swap_size * 8 <= len) : (swap_size *= 4) {} + // swap_len = 4194304; + // while (swap_len * 8 <= len) : (swap_len *= 4) {} // } - if (utils.alloc(swap_size * element_width, alignment)) |swap| { - const block_len = quad_merge(arr_ptr, len, swap, swap_size, 32, cmp, cmp_data, element_width, copy); - _ = block_len; + if (utils.alloc(swap_len * element_width, alignment)) |swap| { + const block_len = quad_merge(arr_ptr, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy); - // TODO: final rotate merge. + rotate_merge(arr_ptr, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy); utils.dealloc(swap, alignment); } else { @@ -113,15 +112,47 @@ fn quadsort_stack_swap( const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy); - _ = block_len; - // TODO: final rotate merge. + rotate_merge(array, len, swap, 512, block_len, cmp, cmp_data, element_width, copy); } // ================ Inplace Rotate Merge ====================================== // These are used as backup if the swap size is not large enough. // Also can be used for the final merge to reduce memory footprint. +fn rotate_merge( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + block_len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, +) void { + var end_ptr = array + len * element_width; + + if (len <= block_len * 2 and len - block_len <= swap_len) { + partial_backwards_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy); + return; + } + + var current_block_len = block_len; + while (current_block_len < len) : (current_block_len *= 2) { + var arr_ptr = array; + while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += current_block_len * 2 * element_width) { + if (@intFromPtr(arr_ptr) + current_block_len * 2 * element_width < @intFromPtr(end_ptr)) { + rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, current_block_len, cmp, cmp_data, element_width, copy); + continue; + } + const right_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width - current_block_len; + rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, right_len, cmp, cmp_data, element_width, copy); + break; + } + } +} + /// Merges two blocks together while only using limited memory. fn rotate_merge_block( array: [*]u8, @@ -149,15 +180,15 @@ fn rotate_merge_block( if (left != 0) { if (left_block + left <= swap_len) { - @memcpy(swap[0 .. left_block * element_width], array[0 .. left_block * element_width]); - @memcpy((swap + left_block * element_width[0 .. left * element_width]), (array + (left_block + right_block) * element_width)[0 .. left * element_width]); + @memcpy(swap[0..(left_block * element_width)], array[0..(left_block * element_width)]); + @memcpy((swap + left_block * element_width)[0..(left * element_width)], (array + (left_block + right_block) * element_width)[0..(left * element_width)]); std.mem.copyBackwards(u8, (array + (left + left_block) * element_width)[0..(right_block * element_width)], (array + left_block * element_width)[0..(right_block * element_width)]); cross_merge(array, swap, left_block, left, cmp, cmp_data, element_width, copy); } else { trinity_rotation(array + left_block * element_width, right_block + left, swap, swap_len, right_block, element_width, copy); - const unbalanced = (left * 2 < left_block) | (left_block * 2 < left); + const unbalanced = (left * 2 < left_block) or (left_block * 2 < left); if (unbalanced and left <= swap_len) { partial_backwards_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy); } else if (unbalanced and left_block <= swap_len) { @@ -169,7 +200,7 @@ fn rotate_merge_block( } if (right != 0) { - const unbalanced = (right * 2 < right_block) | (right_block * 2 < right); + const unbalanced = (right * 2 < right_block) or (right_block * 2 < right); if ((unbalanced and right <= swap_len) or right + right_block <= swap_len) { partial_backwards_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy); } else if (unbalanced and left_block <= swap_len) { @@ -367,6 +398,38 @@ fn trinity_rotation( } } +// TODO: better testing for rotate_merge and rotate_merge_block (or just fuzzing). +test "rotate_merge" { + const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [10]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; + rotate_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; + rotate_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; + rotate_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // Limited swap, can't finish merge + arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; + rotate_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(arr, expected); + + // TODO: should this work? Has too little swap. + // arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; + // rotate_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + // try testing.expectEqual(arr, expected); +} + test "monobound_binary_first" { var arr = [25]i64{ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); From f91a9d716d2c1f534a105cc4ebbe05c5c409266e Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 22:45:26 -0700 Subject: [PATCH 088/203] remove always_tail (breaks wasm) and trust llvm to get it right (it does)...try 2, commit was missing --- crates/compiler/builtins/bitcode/src/sort.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 4d5deb51ccd..57b87117607 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -837,7 +837,7 @@ fn partial_forward_merge_right_head_2( right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); + return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -848,7 +848,7 @@ fn partial_forward_merge_right_head_2( left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); + return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -873,7 +873,7 @@ fn partial_forward_merge_left_head_2( left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_left_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); + return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } @@ -884,7 +884,7 @@ fn partial_forward_merge_left_head_2( right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return @call(.always_tail, partial_forward_merge_right_head_2, .{ dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy }); + return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); } return true; } From afe6128b1f8c4af3aa61e4ae5b68be09afbc81cf Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 24 Jul 2024 23:23:48 -0700 Subject: [PATCH 089/203] fix reversed case --- crates/compiler/builtins/bitcode/src/sort.zig | 145 +++++++++--------- 1 file changed, 71 insertions(+), 74 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 57b87117607..8512f002889 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1391,16 +1391,14 @@ fn quad_swap( arr_ptr += 8 * element_width; continue :outer; }, - // TODO: Figure out why the reversed case below is broken in some cases. - // Note, it seems like the reverse itself is not broken, but the count and pointer become off somehow. - // 15 => { - // // potentially already reverse ordered, check rest! - // if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { - // reverse_head = arr_ptr; - // break :switch_state .reversed; - // } - // break :switch_state .not_ordered; - // }, + 15 => { + // potentially already reverse ordered, check rest! + if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + reverse_head = arr_ptr; + break :switch_state .reversed; + } + break :switch_state .not_ordered; + }, else => { break :switch_state .not_ordered; }, @@ -1491,7 +1489,6 @@ fn quad_swap( } // Just an unorderd block, do it inplace. - inline for ([4]u4{ v1, v2, v3, v4 }) |v| { const x = if (v == 0) element_width else 0; const not_x = if (v != 0) element_width else 0; @@ -1505,7 +1502,7 @@ fn quad_swap( if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); } - arr_ptr += 8; + arr_ptr += 8 * element_width; continue :outer; } @@ -1637,68 +1634,68 @@ fn quad_reversal( } } -// test "quad_swap" { -// var arr: [75]i64 = undefined; -// var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - -// arr = [75]i64{ -// // multiple ordered chunks -// 1, 3, 5, 7, 9, 11, 13, 15, -// // -// 33, 34, 35, 36, 37, 38, 39, 40, -// // partially ordered -// 41, 42, 45, 46, 43, 44, 47, 48, -// // multiple reverse chunks -// 70, 69, 68, 67, 66, 65, 64, 63, -// // -// 16, 14, 12, 10, 8, 6, 4, 2, -// // another ordered -// 49, 50, 51, 52, 53, 54, 55, 56, -// // unordered -// 23, 21, 19, 20, 24, 22, 18, 17, -// // partially reversed -// 32, 31, 28, 27, 30, 29, 26, 25, -// // awkward tail -// 62, 59, 61, 60, 71, 73, 75, 74, -// // -// 72, 58, 57, -// }; - -// var result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); -// try testing.expectEqual(result, .unfinished); -// try testing.expectEqual(arr, [75]i64{ -// // first 32 elements sorted (with 8 reversed that get flipped here) -// 1, 2, 3, 4, 5, 6, 7, 8, -// // -// 9, 10, 11, 12, 13, 14, 15, 16, -// // -// 33, 34, 35, 36, 37, 38, 39, 40, -// // -// 41, 42, 43, 44, 45, 46, 47, 48, -// // second 32 elements sorted (with 8 reversed that get flipped here) -// 17, 18, 19, 20, 21, 22, 23, 24, -// // -// 25, 26, 27, 28, 29, 30, 31, 32, -// // -// 49, 50, 51, 52, 53, 54, 55, 56, -// // -// 63, 64, 65, 66, 67, 68, 69, 70, -// // awkward tail -// 57, 58, 59, 60, 61, 62, 71, 72, -// // -// 73, 74, 75, -// }); - -// // Just reversed. -// var expected: [75]i64 = undefined; -// for (0..75) |i| { -// expected[i] = @intCast(i + 1); -// arr[i] = @intCast(75 - i); -// } -// result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); -// try testing.expectEqual(result, .sorted); -// try testing.expectEqual(arr, expected); -// } +test "quad_swap" { + var arr: [75]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [75]i64{ + // multiple ordered chunks + 1, 3, 5, 7, 9, 11, 13, 15, + // + 33, 34, 35, 36, 37, 38, 39, 40, + // partially ordered + 41, 42, 45, 46, 43, 44, 47, 48, + // multiple reverse chunks + 70, 69, 68, 67, 66, 65, 64, 63, + // + 16, 14, 12, 10, 8, 6, 4, 2, + // another ordered + 49, 50, 51, 52, 53, 54, 55, 56, + // unordered + 23, 21, 19, 20, 24, 22, 18, 17, + // partially reversed + 32, 31, 28, 27, 30, 29, 26, 25, + // awkward tail + 62, 59, 61, 60, 71, 73, 75, 74, + // + 72, 58, 57, + }; + + var result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(result, .unfinished); + try testing.expectEqual(arr, [75]i64{ + // first 32 elements sorted (with 8 reversed that get flipped here) + 1, 2, 3, 4, 5, 6, 7, 8, + // + 9, 10, 11, 12, 13, 14, 15, 16, + // + 33, 34, 35, 36, 37, 38, 39, 40, + // + 41, 42, 43, 44, 45, 46, 47, 48, + // second 32 elements sorted (with 8 reversed that get flipped here) + 17, 18, 19, 20, 21, 22, 23, 24, + // + 25, 26, 27, 28, 29, 30, 31, 32, + // + 49, 50, 51, 52, 53, 54, 55, 56, + // + 63, 64, 65, 66, 67, 68, 69, 70, + // awkward tail + 57, 58, 59, 60, 61, 62, 71, 72, + // + 73, 74, 75, + }); + + // Just reversed. + var expected: [75]i64 = undefined; + for (0..75) |i| { + expected[i] = @intCast(i + 1); + arr[i] = @intCast(75 - i); + } + result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + try testing.expectEqual(result, .sorted); + try testing.expectEqual(arr, expected); +} test "quad_swap_merge" { var arr: [8]i64 = undefined; From 7373a76f8177392aa5564b4a20ab1be518793ba6 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 25 Jul 2024 18:34:04 -0700 Subject: [PATCH 090/203] add running sort directly for zig for testing --- .../builtins/bitcode/src/fuzz_sort.zig | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 crates/compiler/builtins/bitcode/src/fuzz_sort.zig diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig new file mode 100644 index 00000000000..ef7cf36067b --- /dev/null +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -0,0 +1,78 @@ +const std = @import("std"); +const sort = @import("sort.zig"); + +pub fn main() !void { + const size = 1000000; + var arr_ptr: [*]i64 = @alignCast(@ptrCast(testing_roc_alloc(size * @sizeOf(i64), @alignOf(i64)))); + defer testing_roc_dealloc(arr_ptr, @alignOf(i64)); + + for (0..size) |i| { + arr_ptr[i] = @intCast(i + 1); + } + + const seed = 42; + var rng = std.rand.DefaultPrng.init(seed); + rng.random().shuffle(i64, arr_ptr[0..size]); + + var timer = try std.time.Timer.start(); + sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare, null, false, &test_i64_inc_n, @sizeOf(i64), @alignOf(i64), &test_i64_copy); + const elapsed: f64 = @floatFromInt(timer.read()); + std.debug.print("Time elapsed is: {d:.3}ms\n", .{ + elapsed / std.time.ns_per_ms, + }); +} + +const Opaque = ?[*]u8; +fn test_i64_compare(_: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { + const a = @as(*i64, @alignCast(@ptrCast(a_ptr))).*; + const b = @as(*i64, @alignCast(@ptrCast(b_ptr))).*; + + const gt = @as(u8, @intFromBool(a > b)); + const lt = @as(u8, @intFromBool(a < b)); + + // Eq = 0 + // GT = 1 + // LT = 2 + return lt + lt + gt; +} + +fn test_i64_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { + @as(*i64, @alignCast(@ptrCast(dst_ptr))).* = @as(*i64, @alignCast(@ptrCast(src_ptr))).*; +} + +fn test_i64_inc_n(_: ?[*]u8, _: usize) callconv(.C) void {} + +comptime { + @export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong }); + @export(testing_roc_dealloc, .{ .name = "roc_dealloc", .linkage = .Strong }); + @export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong }); +} + +var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +const allocator = gpa.allocator(); + +fn testing_roc_alloc(size: usize, _: u32) callconv(.C) ?*anyopaque { + // We store an extra usize which is the size of the full allocation. + const full_size = size + @sizeOf(usize); + var raw_ptr = (allocator.alloc(u8, full_size) catch unreachable).ptr; + @as([*]usize, @alignCast(@ptrCast(raw_ptr)))[0] = full_size; + raw_ptr += @sizeOf(usize); + const ptr = @as(?*anyopaque, @ptrCast(raw_ptr)); + + return ptr; +} + +fn testing_roc_dealloc(c_ptr: *anyopaque, _: u32) callconv(.C) void { + const raw_ptr = @as([*]u8, @ptrCast(c_ptr)) - @sizeOf(usize); + const full_size = @as([*]usize, @alignCast(@ptrCast(raw_ptr)))[0]; + const slice = raw_ptr[0..full_size]; + + allocator.free(slice); +} + +fn testing_roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void { + _ = c_ptr; + _ = tag_id; + + @panic("Roc panicked"); +} From 52f6c30173f78d434e662cbb357543e61b583dac Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 25 Jul 2024 18:42:34 -0700 Subject: [PATCH 091/203] init allocator in main --- crates/compiler/builtins/bitcode/src/fuzz_sort.zig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index ef7cf36067b..5981b4fde87 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -1,7 +1,13 @@ const std = @import("std"); const sort = @import("sort.zig"); +var gpa: std.heap.GeneralPurposeAllocator(.{}) = undefined; +var allocator: std.mem.Allocator = undefined; + pub fn main() !void { + gpa = .{}; + allocator = gpa.allocator(); + const size = 1000000; var arr_ptr: [*]i64 = @alignCast(@ptrCast(testing_roc_alloc(size * @sizeOf(i64), @alignOf(i64)))); defer testing_roc_dealloc(arr_ptr, @alignOf(i64)); @@ -48,9 +54,6 @@ comptime { @export(testing_roc_panic, .{ .name = "roc_panic", .linkage = .Strong }); } -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; -const allocator = gpa.allocator(); - fn testing_roc_alloc(size: usize, _: u32) callconv(.C) ?*anyopaque { // We store an extra usize which is the size of the full allocation. const full_size = size + @sizeOf(usize); From b73b70b6b2b6e7c8d881e754e446ca8ea35836d7 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 25 Jul 2024 22:45:54 -0700 Subject: [PATCH 092/203] fix sort bug --- crates/compiler/builtins/bitcode/src/sort.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 8512f002889..7766e53f329 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -133,7 +133,7 @@ fn rotate_merge( ) void { var end_ptr = array + len * element_width; - if (len <= block_len * 2 and len - block_len <= swap_len) { + if (len <= block_len * 2 and len -% block_len <= swap_len) { partial_backwards_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy); return; } @@ -1129,7 +1129,9 @@ fn cross_merge( var dest_head = dest; var dest_tail = dest + (left_len + right_len - 1) * element_width; - outer: while (true) { + // TODO: For fluxsort, this can be while(true), for quadsort, it needs extra protection. + // That or I have a bug...not fully sure. + outer: while (@intFromPtr(left_tail) - @intFromPtr(left_head) > 8 * element_width and @intFromPtr(right_tail) - @intFromPtr(right_head) > 8 * element_width) { if (@intFromPtr(left_tail) - @intFromPtr(left_head) > 8 * element_width) { // 8 elements all less than or equal to and can be moved together. while (compare(cmp, cmp_data, left_head + 7 * element_width, right_head) != GT) { From f9abfcbb16f38f8b60df13235c5bf6d81c1cf8ae Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 25 Jul 2024 23:18:20 -0700 Subject: [PATCH 093/203] switch to isize subtraction for correctness --- crates/compiler/builtins/bitcode/src/sort.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 7766e53f329..3d9194a316f 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1129,10 +1129,9 @@ fn cross_merge( var dest_head = dest; var dest_tail = dest + (left_len + right_len - 1) * element_width; - // TODO: For fluxsort, this can be while(true), for quadsort, it needs extra protection. - // That or I have a bug...not fully sure. - outer: while (@intFromPtr(left_tail) - @intFromPtr(left_head) > 8 * element_width and @intFromPtr(right_tail) - @intFromPtr(right_head) > 8 * element_width) { - if (@intFromPtr(left_tail) - @intFromPtr(left_head) > 8 * element_width) { + outer: while (true) { + // This has to be allowed to go negative to be correct. Thus, isize. + if (@as(isize, @intCast(@intFromPtr(left_tail))) - @as(isize, @intCast(@intFromPtr(left_head))) > @as(isize, @intCast(8 * element_width))) { // 8 elements all less than or equal to and can be moved together. while (compare(cmp, cmp_data, left_head + 7 * element_width, right_head) != GT) { inline for (0..8) |_| { @@ -1158,7 +1157,8 @@ fn cross_merge( } // Attempt to do the same for the right list. - if (@intFromPtr(right_tail) - @intFromPtr(right_head) > 8 * element_width) { + // This has to be allowed to go negative to be correct. Thus, isize. + if (@as(isize, @intCast(@intFromPtr(right_tail))) - @as(isize, @intCast(@intFromPtr(right_head))) > @as(isize, @intCast(8 * element_width))) { // left greater than 8 elements right and can be moved together. while (compare(cmp, cmp_data, left_head, right_head + 7 * element_width) == GT) { inline for (0..8) |_| { From 7254122d3017ff656775d9063cb67d47870ee72b Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 25 Jul 2024 23:25:04 -0700 Subject: [PATCH 094/203] turn a few while loops into for loops for clarity --- crates/compiler/builtins/bitcode/src/sort.zig | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 3d9194a316f..ac2926b7256 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1187,14 +1187,9 @@ fn cross_merge( break; // Large enough to warrent a two way merge. - var loops: usize = 8; - while (true) { + for (0..8) |_| { head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); - - loops -= 1; - if (loops == 0) - break; } } @@ -1811,8 +1806,7 @@ fn parity_merge( } head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); - var ll = left_len - 1; - while (ll != 0) : (ll -= 1) { + for (0..(left_len - 1)) |_| { head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } From f4cbf9278b81d3461399bda68b8daea4ab3b3619 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 25 Jul 2024 23:58:56 -0700 Subject: [PATCH 095/203] cleanup fuzz slice --- .../builtins/bitcode/src/fuzz_sort.zig | 30 +++++-------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index 5981b4fde87..32003eb37eb 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -1,24 +1,19 @@ const std = @import("std"); const sort = @import("sort.zig"); -var gpa: std.heap.GeneralPurposeAllocator(.{}) = undefined; -var allocator: std.mem.Allocator = undefined; +extern fn malloc(size: usize) callconv(.C) ?*anyopaque; +extern fn free(c_ptr: *anyopaque) callconv(.C) void; pub fn main() !void { - gpa = .{}; - allocator = gpa.allocator(); - const size = 1000000; var arr_ptr: [*]i64 = @alignCast(@ptrCast(testing_roc_alloc(size * @sizeOf(i64), @alignOf(i64)))); defer testing_roc_dealloc(arr_ptr, @alignOf(i64)); - for (0..size) |i| { - arr_ptr[i] = @intCast(i + 1); - } - const seed = 42; var rng = std.rand.DefaultPrng.init(seed); - rng.random().shuffle(i64, arr_ptr[0..size]); + for (0..size) |i| { + arr_ptr[i] = rng.random().int(i64); + } var timer = try std.time.Timer.start(); sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare, null, false, &test_i64_inc_n, @sizeOf(i64), @alignOf(i64), &test_i64_copy); @@ -55,22 +50,11 @@ comptime { } fn testing_roc_alloc(size: usize, _: u32) callconv(.C) ?*anyopaque { - // We store an extra usize which is the size of the full allocation. - const full_size = size + @sizeOf(usize); - var raw_ptr = (allocator.alloc(u8, full_size) catch unreachable).ptr; - @as([*]usize, @alignCast(@ptrCast(raw_ptr)))[0] = full_size; - raw_ptr += @sizeOf(usize); - const ptr = @as(?*anyopaque, @ptrCast(raw_ptr)); - - return ptr; + return malloc(size); } fn testing_roc_dealloc(c_ptr: *anyopaque, _: u32) callconv(.C) void { - const raw_ptr = @as([*]u8, @ptrCast(c_ptr)) - @sizeOf(usize); - const full_size = @as([*]usize, @alignCast(@ptrCast(raw_ptr)))[0]; - const slice = raw_ptr[0..full_size]; - - allocator.free(slice); + free(c_ptr); } fn testing_roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void { From 0aaf9e2529a61c2829a2d38ad9ab5e0338746e2c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 00:24:27 -0700 Subject: [PATCH 096/203] start converting to real fuzzer --- .../builtins/bitcode/src/fuzz_sort.zig | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index 32003eb37eb..c9ea0709347 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -4,23 +4,34 @@ const sort = @import("sort.zig"); extern fn malloc(size: usize) callconv(.C) ?*anyopaque; extern fn free(c_ptr: *anyopaque) callconv(.C) void; -pub fn main() !void { - const size = 1000000; - var arr_ptr: [*]i64 = @alignCast(@ptrCast(testing_roc_alloc(size * @sizeOf(i64), @alignOf(i64)))); - defer testing_roc_dealloc(arr_ptr, @alignOf(i64)); - - const seed = 42; - var rng = std.rand.DefaultPrng.init(seed); - for (0..size) |i| { - arr_ptr[i] = rng.random().int(i64); - } - - var timer = try std.time.Timer.start(); +fn cMain() callconv(.C) void { + fuzz_main() catch unreachable; +} + +comptime { + @export(cMain, .{ .name = "main", .linkage = .Strong }); +} + +var allocator: std.mem.Allocator = undefined; + +pub fn fuzz_main() !void { + // Setup an allocator that will detect leaks/use-after-free/etc + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + // this will check for leaks and crash the program if it finds any + defer std.debug.assert(gpa.deinit() == .ok); + allocator = gpa.allocator(); + + // Read the data from stdin + const stdin = std.io.getStdIn(); + const data = try stdin.readToEndAlloc(allocator, std.math.maxInt(usize)); + defer allocator.free(data); + + const size = data.len / @sizeOf(i64); + const arr_ptr: [*]i64 = @alignCast(@ptrCast(data.ptr)); + sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare, null, false, &test_i64_inc_n, @sizeOf(i64), @alignOf(i64), &test_i64_copy); - const elapsed: f64 = @floatFromInt(timer.read()); - std.debug.print("Time elapsed is: {d:.3}ms\n", .{ - elapsed / std.time.ns_per_ms, - }); + + std.debug.assert(std.sort.isSorted(i64, arr_ptr[0..size], {}, std.sort.asc(i64))); } const Opaque = ?[*]u8; @@ -50,11 +61,19 @@ comptime { } fn testing_roc_alloc(size: usize, _: u32) callconv(.C) ?*anyopaque { - return malloc(size); + // We store an extra usize which is the size of the full allocation. + const full_size = size + @sizeOf(usize); + var raw_ptr = (allocator.alloc(u8, full_size) catch unreachable).ptr; + @as([*]usize, @alignCast(@ptrCast(raw_ptr)))[0] = full_size; + raw_ptr += @sizeOf(usize); + return @as(?*anyopaque, @ptrCast(raw_ptr)); } fn testing_roc_dealloc(c_ptr: *anyopaque, _: u32) callconv(.C) void { - free(c_ptr); + const raw_ptr = @as([*]u8, @ptrCast(c_ptr)) - @sizeOf(usize); + const full_size = @as([*]usize, @alignCast(@ptrCast(raw_ptr)))[0]; + const slice = raw_ptr[0..full_size]; + allocator.free(slice); } fn testing_roc_panic(c_ptr: *anyopaque, tag_id: u32) callconv(.C) void { From a0dd18b0fc78f2d82db979783a53aa3bf69040d7 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 00:43:11 -0700 Subject: [PATCH 097/203] fix potential crash case --- crates/compiler/builtins/bitcode/src/sort.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index ac2926b7256..04d2cd3ab59 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1520,7 +1520,7 @@ fn quad_swap( break :reverse_block; if (rem >= 1 and compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width) != GT) break :reverse_block; - quad_reversal(reverse_head, arr_ptr + (rem - 1) * element_width, element_width, copy); + quad_reversal(reverse_head, arr_ptr + rem * element_width - element_width, element_width, copy); // If we just reversed the entire array, it is sorted. if (reverse_head == array) From 6ef44a22c8054ae99d000beaba3a0cecbfa7bf6a Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 00:59:26 -0700 Subject: [PATCH 098/203] add fuzzing instructions --- crates/compiler/builtins/bitcode/src/fuzz_sort.zig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index c9ea0709347..c81199545b6 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -1,3 +1,10 @@ +/// Sort Fuzzer! +/// To fuzz: On linux, first install afl++. +/// Then build this with: +/// zig build-lib -static -fcompiler-rt -flto -fPIC src/fuzz_sort.zig +/// afl-clang-lto -o fuzz libfuzz_sort.a +/// Finally, run with afl +/// afl-fuzz -i input -o output -- ./fuzz const std = @import("std"); const sort = @import("sort.zig"); From 67241e8f6b475614b34a91f525f4d1a812916192 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 15:29:12 -0700 Subject: [PATCH 099/203] ignore static libs --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f226ee50658..3eba8a159b5 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ zig-cache .envrc *.rs.bk *.o +*.a *.so *.so.* *.obj From 383259e55f53a59769a02b65ef39fdb7ef8ba373 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 18:07:08 -0700 Subject: [PATCH 100/203] add fuzzing script --- crates/compiler/builtins/bitcode/.gitignore | 1 + .../compiler/builtins/bitcode/fuzz_in_tmux.sh | 42 +++++++++++++++++++ .../builtins/bitcode/src/fuzz_sort.zig | 19 +++++---- 3 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 crates/compiler/builtins/bitcode/.gitignore create mode 100755 crates/compiler/builtins/bitcode/fuzz_in_tmux.sh diff --git a/crates/compiler/builtins/bitcode/.gitignore b/crates/compiler/builtins/bitcode/.gitignore new file mode 100644 index 00000000000..d1de0dc8054 --- /dev/null +++ b/crates/compiler/builtins/bitcode/.gitignore @@ -0,0 +1 @@ +.fuzz_data diff --git a/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh b/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh new file mode 100755 index 00000000000..d4614456da7 --- /dev/null +++ b/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -euxo pipefail + +# Run from this directory. +SCRIPT_RELATIVE_DIR=`dirname "${BASH_SOURCE[0]}"` +cd $SCRIPT_RELATIVE_DIR + +# First compile the fuzz target. +zig build-lib -static -fcompiler-rt -flto -fPIC src/fuzz_sort.zig +afl-clang-lto -o fuzz libfuzz_sort.a +AFL_LLVM_CMPLOG=1 afl-clang-lto -o fuzz-cmplog libfuzz_sort.a +AFL_LLVM_LAF_ALL=1 afl-clang-lto -o fuzz-cmpcov libfuzz_sort.a + +# Setup fuzz directory with dummy input. +INPUT_DIR='.fuzz_data/input' +OUTPUT_DIR='.fuzz_data/output' +if [ ! -d .fuzz_data ]; then + mkdir -p $INPUT_DIR + echo '1234567887654321' > $INPUT_DIR/dummy_input +else + # Resuming from existing run. + INPUT_DIR='-' +fi + +# Just hardcoding to 7 fuzzers (this avoids overwhelming 8 core machines). +BASE_CMD="AFL_TESTCACHE_SIZE=250 AFL_IMPORT_FIRST=1 afl-fuzz -i $INPUT_DIR -o $OUTPUT_DIR" + +# I'm trying to follow the guide around secondary fuzzers, but I don't quite follow the wording. +# So I feel this may be correct, but it may also be more random then they expect. +# Overkill anyway...so this is fine. +tmux new-session -d -s "fuzz" "AFL_FINAL_SYNC=1 $BASE_CMD -M fuzzer01 ./fuzz" +tmux split-window -h "$BASE_CMD -S fuzzer02 -c ./fuzz-cmplog -m none -l 2AT -p explore ./fuzz" +tmux split-window -v -t 0.0 "$BASE_CMD -S fuzzer03 -c ./fuzz-cmplog -m none -L 0 -p exploit ./fuzz" +tmux split-window -v -t 0.2 "$BASE_CMD -S fuzzer04 -p explore ./fuzz-cmpcov" +tmux new-window "$BASE_CMD -S fuzzer05 -Z -p coe ./fuzz-cmpcov" +tmux split-window -h "$BASE_CMD -S fuzzer06 -P exploit ./fuzz" +tmux split-window -v -t 1.0 "AFL_DISABLE_TRIM=1 $BASE_CMD -S fuzzer07 -p explore ./fuzz" +tmux split-window -v -t 1.2 "htop" +tmux new-window +tmux send-keys "afl-whatsup -d $OUTPUT_DIR" +tmux select-window -t 0 +tmux -2 a -t "fuzz" diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index c81199545b6..833d21941c5 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -1,10 +1,3 @@ -/// Sort Fuzzer! -/// To fuzz: On linux, first install afl++. -/// Then build this with: -/// zig build-lib -static -fcompiler-rt -flto -fPIC src/fuzz_sort.zig -/// afl-clang-lto -o fuzz libfuzz_sort.a -/// Finally, run with afl -/// afl-fuzz -i input -o output -- ./fuzz const std = @import("std"); const sort = @import("sort.zig"); @@ -19,6 +12,8 @@ comptime { @export(cMain, .{ .name = "main", .linkage = .Strong }); } +const DEBUG = false; + var allocator: std.mem.Allocator = undefined; pub fn fuzz_main() !void { @@ -36,9 +31,17 @@ pub fn fuzz_main() !void { const size = data.len / @sizeOf(i64); const arr_ptr: [*]i64 = @alignCast(@ptrCast(data.ptr)); + if (DEBUG) { + std.debug.print("Input: [{d}]{d}\n", .{ size, arr_ptr[0..size] }); + } + sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare, null, false, &test_i64_inc_n, @sizeOf(i64), @alignOf(i64), &test_i64_copy); - std.debug.assert(std.sort.isSorted(i64, arr_ptr[0..size], {}, std.sort.asc(i64))); + const sorted = std.sort.isSorted(i64, arr_ptr[0..size], {}, std.sort.asc(i64)); + if (DEBUG) { + std.debug.print("Output: [{d}]{d}\nSorted: {}\n", .{ size, arr_ptr[0..size], sorted }); + } + std.debug.assert(sorted); } const Opaque = ?[*]u8; From b3d30e92826ab135ac7e5af6cf9d686c0c2f8471 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 18:11:28 -0700 Subject: [PATCH 101/203] correct assert --- crates/compiler/builtins/bitcode/fuzz_in_tmux.sh | 1 + crates/compiler/builtins/bitcode/src/sort.zig | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh b/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh index d4614456da7..6763eb21b83 100755 --- a/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh +++ b/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh @@ -38,5 +38,6 @@ tmux split-window -v -t 1.0 "AFL_DISABLE_TRIM=1 $BASE_CMD -S fuzzer07 -p explore tmux split-window -v -t 1.2 "htop" tmux new-window tmux send-keys "afl-whatsup -d $OUTPUT_DIR" +tmux select-window -t 1 tmux select-window -t 0 tmux -2 a -t "fuzz" diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 04d2cd3ab59..fc7f111eda1 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1780,7 +1780,7 @@ fn tail_swap( } /// Merges two neighboring sorted arrays into dest. -/// Left must be equal to or 1 smaller than right. +/// Left and right length mus be same or within 1 element. fn parity_merge( dest: [*]u8, src: [*]u8, @@ -1791,7 +1791,7 @@ fn parity_merge( element_width: usize, copy: CopyFn, ) void { - std.debug.assert(left_len == right_len or left_len == right_len - 1); + std.debug.assert(left_len == right_len or left_len == right_len - 1 or left_len - 1 == right_len); var left_head = src; var right_head = src + left_len * element_width; From 47017179a6e1155ea84fa1cc980e1b8a09743081 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Fri, 26 Jul 2024 18:40:02 -0700 Subject: [PATCH 102/203] watch afl status --- crates/compiler/builtins/bitcode/fuzz_in_tmux.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh b/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh index 6763eb21b83..cb9a5d4586d 100755 --- a/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh +++ b/crates/compiler/builtins/bitcode/fuzz_in_tmux.sh @@ -36,8 +36,7 @@ tmux new-window "$BASE_CMD -S fuzzer05 -Z -p coe ./fuzz-cmpcov" tmux split-window -h "$BASE_CMD -S fuzzer06 -P exploit ./fuzz" tmux split-window -v -t 1.0 "AFL_DISABLE_TRIM=1 $BASE_CMD -S fuzzer07 -p explore ./fuzz" tmux split-window -v -t 1.2 "htop" -tmux new-window -tmux send-keys "afl-whatsup -d $OUTPUT_DIR" +tmux new-window "watch -c -n 30 afl-whatsup -s .fuzz_data/output" tmux select-window -t 1 tmux select-window -t 0 tmux -2 a -t "fuzz" From 0e092e0225b9a225f9af31533f1c28739d3348e1 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 00:45:46 -0700 Subject: [PATCH 103/203] add beginining of indirect sorting --- crates/compiler/builtins/bitcode/src/sort.zig | 64 ++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index fc7f111eda1..f801f3c72af 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -46,10 +46,66 @@ pub fn quadsort( // Then have our builtin dispatch to the correct version. // llvm garbage collection would remove all other variants. // Also, for numeric types, inlining the compare function can be a 2x perf gain. - if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { + if (element_width <= MAX_ELEMENT_BUFFER_SIZE and false) { quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); } else { - roc_panic("todo: fallback to an indirect pointer sort", 0); + if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { + // Build list of pointers to sort. + if (@import("builtin").target.cpu.arch != .wasm32) { + std.debug.print("Allocated! filling array.\n", .{}); + } + var arr_ptr = @as([*]Opaque, @ptrCast(@alignCast(alloc_ptr))); + defer utils.dealloc(alloc_ptr, @alignOf(usize)); + for (0..len) |i| { + arr_ptr[i] = source_ptr + i * element_width; + } + + // Create indirect compare function. + if (@import("builtin").target.cpu.arch != .wasm32) { + std.debug.print("Creating indirect sort!\n", .{}); + } + const IndirectSort = struct { + compare: CompareFn, + pub fn indirect_compare(compare_data: Opaque, lhs_ptr: Opaque, rhs_ptr: Opaque) callconv(.C) u8 { + const lhs = @as(*Opaque, @ptrCast(@alignCast(lhs_ptr))).*; + const rhs = @as(*Opaque, @ptrCast(@alignCast(rhs_ptr))).*; + return compare(compare_data, lhs, rhs); + } + }; + const indirect_sort = IndirectSort{ .compare = cmp }; + + // Sort. + if (@import("builtin").target.cpu.arch != .wasm32) { + std.debug.print("Sorting!\n", .{}); + } + quadsort_direct(@ptrCast(arr_ptr), len, indirect_sort.compare, cmp_data, data_is_owned, inc_n_data, @sizeOf(usize), @alignOf(usize), &pointer_copy); + + if (utils.alloc(len * element_width, alignment)) |collect_alloc_ptr| { + if (@import("builtin").target.cpu.arch != .wasm32) { + std.debug.print("Allocated2! Collecting\n", .{}); + } + // Collect sorted pointers into correct order. + var collect_ptr = collect_alloc_ptr; + defer utils.dealloc(collect_alloc_ptr, alignment); + for (0..len) |i| { + copy(collect_ptr, arr_ptr[i]); + collect_ptr += element_width; + } + + // Copy to original array as sorted. + if (@import("builtin").target.cpu.arch != .wasm32) { + std.debug.print("Copy out!\n", .{}); + } + @memcpy(source_ptr[0..(len * element_width)], collect_ptr[0..(len * element_width)]); + if (@import("builtin").target.cpu.arch != .wasm32) { + std.debug.print("Sorted:[{d}]{d}\nAll done!\n", .{ len, @as([*]i64, @ptrCast(@alignCast(source_ptr)))[0..len] }); + } + } else { + roc_panic("Out of memory while trying to allocate for sorting", 0); + } + } else { + roc_panic("Out of memory while trying to allocate for sorting", 0); + } } } @@ -2440,6 +2496,10 @@ test "swap" { try testing.expectEqual(arr, [2]i64{ -22, -22 }); } +pub fn pointer_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { + @as(*usize, @alignCast(@ptrCast(dst_ptr))).* = @as(*usize, @alignCast(@ptrCast(src_ptr))).*; +} + fn test_i64_compare(_: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { const a = @as(*i64, @alignCast(@ptrCast(a_ptr))).*; const b = @as(*i64, @alignCast(@ptrCast(b_ptr))).*; From 3c510caf30828dff65aed289e1e0529a97513c26 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 01:48:15 -0700 Subject: [PATCH 104/203] git indirect sorting working --- crates/compiler/builtins/bitcode/src/sort.zig | 44 ++++++------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index f801f3c72af..933b07968d0 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -51,55 +51,29 @@ pub fn quadsort( } else { if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { // Build list of pointers to sort. - if (@import("builtin").target.cpu.arch != .wasm32) { - std.debug.print("Allocated! filling array.\n", .{}); - } var arr_ptr = @as([*]Opaque, @ptrCast(@alignCast(alloc_ptr))); defer utils.dealloc(alloc_ptr, @alignOf(usize)); for (0..len) |i| { arr_ptr[i] = source_ptr + i * element_width; } - // Create indirect compare function. - if (@import("builtin").target.cpu.arch != .wasm32) { - std.debug.print("Creating indirect sort!\n", .{}); - } - const IndirectSort = struct { - compare: CompareFn, - pub fn indirect_compare(compare_data: Opaque, lhs_ptr: Opaque, rhs_ptr: Opaque) callconv(.C) u8 { - const lhs = @as(*Opaque, @ptrCast(@alignCast(lhs_ptr))).*; - const rhs = @as(*Opaque, @ptrCast(@alignCast(rhs_ptr))).*; - return compare(compare_data, lhs, rhs); - } - }; - const indirect_sort = IndirectSort{ .compare = cmp }; + // Setup for indirect comparison. + inner_cmp = cmp; + defer inner_cmp = null; // Sort. - if (@import("builtin").target.cpu.arch != .wasm32) { - std.debug.print("Sorting!\n", .{}); - } - quadsort_direct(@ptrCast(arr_ptr), len, indirect_sort.compare, cmp_data, data_is_owned, inc_n_data, @sizeOf(usize), @alignOf(usize), &pointer_copy); + quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, data_is_owned, inc_n_data, @sizeOf(usize), @alignOf(usize), &pointer_copy); if (utils.alloc(len * element_width, alignment)) |collect_alloc_ptr| { - if (@import("builtin").target.cpu.arch != .wasm32) { - std.debug.print("Allocated2! Collecting\n", .{}); - } // Collect sorted pointers into correct order. var collect_ptr = collect_alloc_ptr; defer utils.dealloc(collect_alloc_ptr, alignment); for (0..len) |i| { - copy(collect_ptr, arr_ptr[i]); - collect_ptr += element_width; + copy(collect_ptr + i * element_width, arr_ptr[i]); } // Copy to original array as sorted. - if (@import("builtin").target.cpu.arch != .wasm32) { - std.debug.print("Copy out!\n", .{}); - } @memcpy(source_ptr[0..(len * element_width)], collect_ptr[0..(len * element_width)]); - if (@import("builtin").target.cpu.arch != .wasm32) { - std.debug.print("Sorted:[{d}]{d}\nAll done!\n", .{ len, @as([*]i64, @ptrCast(@alignCast(source_ptr)))[0..len] }); - } } else { roc_panic("Out of memory while trying to allocate for sorting", 0); } @@ -2496,6 +2470,14 @@ test "swap" { try testing.expectEqual(arr, [2]i64{ -22, -22 }); } +// While I think it is technically safe, I'm not a fan of using a threadlocal for this. +threadlocal var inner_cmp: ?CompareFn = null; +pub fn indirect_compare(compare_data: Opaque, lhs_ptr: Opaque, rhs_ptr: Opaque) callconv(.C) u8 { + const lhs = @as(*[*]u8, @ptrCast(@alignCast(lhs_ptr))).*; + const rhs = @as(*[*]u8, @ptrCast(@alignCast(rhs_ptr))).*; + return (inner_cmp orelse unreachable)(compare_data, lhs, rhs); +} + pub fn pointer_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { @as(*usize, @alignCast(@ptrCast(dst_ptr))).* = @as(*usize, @alignCast(@ptrCast(src_ptr))).*; } From 8ace0dd91b9a509514f29006fecfa6902c0bb25f Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 01:52:27 -0700 Subject: [PATCH 105/203] reenable direct sorting --- crates/compiler/builtins/bitcode/src/sort.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 933b07968d0..e6715a6aee4 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -46,7 +46,7 @@ pub fn quadsort( // Then have our builtin dispatch to the correct version. // llvm garbage collection would remove all other variants. // Also, for numeric types, inlining the compare function can be a 2x perf gain. - if (element_width <= MAX_ELEMENT_BUFFER_SIZE and false) { + if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); } else { if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { From b1ff39fd6f75164a038851f466e52ea665cec0d2 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 01:54:33 -0700 Subject: [PATCH 106/203] remove redundant variable --- crates/compiler/builtins/bitcode/src/sort.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index e6715a6aee4..04acba8904e 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -64,10 +64,9 @@ pub fn quadsort( // Sort. quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, data_is_owned, inc_n_data, @sizeOf(usize), @alignOf(usize), &pointer_copy); - if (utils.alloc(len * element_width, alignment)) |collect_alloc_ptr| { + if (utils.alloc(len * element_width, alignment)) |collect_ptr| { // Collect sorted pointers into correct order. - var collect_ptr = collect_alloc_ptr; - defer utils.dealloc(collect_alloc_ptr, alignment); + defer utils.dealloc(collect_ptr, alignment); for (0..len) |i| { copy(collect_ptr + i * element_width, arr_ptr[i]); } From 15daad66fed3513d915577207b54ee0fa8a9e690 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 16:59:00 -0700 Subject: [PATCH 107/203] wire refcounting through sorting with comptime bool --- crates/compiler/builtins/bitcode/src/sort.zig | 634 +++++++++++++----- 1 file changed, 477 insertions(+), 157 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 04acba8904e..e84e6877ad8 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -34,7 +34,7 @@ pub fn quadsort( len: usize, cmp: CompareFn, cmp_data: Opaque, - data_is_owned: bool, + data_is_owned_runtime: bool, inc_n_data: IncN, element_width: usize, alignment: u32, @@ -47,7 +47,11 @@ pub fn quadsort( // llvm garbage collection would remove all other variants. // Also, for numeric types, inlining the compare function can be a 2x perf gain. if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { - quadsort_direct(source_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); + if (data_is_owned_runtime) { + quadsort_direct(source_ptr, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); + } else { + quadsort_direct(source_ptr, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data); + } } else { if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { // Build list of pointers to sort. @@ -62,7 +66,11 @@ pub fn quadsort( defer inner_cmp = null; // Sort. - quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, data_is_owned, inc_n_data, @sizeOf(usize), @alignOf(usize), &pointer_copy); + if (data_is_owned_runtime) { + quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, true, inc_n_data); + } else { + quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, false, inc_n_data); + } if (utils.alloc(len * element_width, alignment)) |collect_ptr| { // Collect sorted pointers into correct order. @@ -87,11 +95,11 @@ fn quadsort_direct( len: usize, cmp: CompareFn, cmp_data: Opaque, - data_is_owned: bool, - inc_n_data: IncN, element_width: usize, alignment: u32, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { var arr_ptr = array; if (len < 32) { @@ -101,8 +109,8 @@ fn quadsort_direct( // Also, zig doesn't hav alloca, so we always do max size here. var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 32]u8 align(BufferAlign) = undefined; const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); - tail_swap(arr_ptr, len, swap, cmp, cmp_data, element_width, copy); - } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy) != .sorted) { + tail_swap(arr_ptr, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data) != .sorted) { var swap_len = len; // This is optional, for about 5% perf hit, lower memory usage on large arrays. @@ -112,9 +120,9 @@ fn quadsort_direct( // } if (utils.alloc(swap_len * element_width, alignment)) |swap| { - const block_len = quad_merge(arr_ptr, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy); + const block_len = quad_merge(arr_ptr, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - rotate_merge(arr_ptr, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy); + rotate_merge(arr_ptr, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); utils.dealloc(swap, alignment); } else { @@ -129,20 +137,18 @@ fn quadsort_stack_swap( len: usize, cmp: CompareFn, cmp_data: Opaque, - data_is_owned: bool, + comptime data_is_owned: bool, inc_n_data: IncN, element_width: usize, copy: CopyFn, ) void { - _ = inc_n_data; - _ = data_is_owned; // Use a 512 element on stack swap buffer. var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 512]u8 align(BufferAlign) = undefined; const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); - const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy); + const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - rotate_merge(array, len, swap, 512, block_len, cmp, cmp_data, element_width, copy); + rotate_merge(array, len, swap, 512, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } // ================ Inplace Rotate Merge ====================================== @@ -159,11 +165,13 @@ fn rotate_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { var end_ptr = array + len * element_width; if (len <= block_len * 2 and len -% block_len <= swap_len) { - partial_backwards_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy); + partial_backwards_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return; } @@ -172,11 +180,11 @@ fn rotate_merge( var arr_ptr = array; while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += current_block_len * 2 * element_width) { if (@intFromPtr(arr_ptr) + current_block_len * 2 * element_width < @intFromPtr(end_ptr)) { - rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, current_block_len, cmp, cmp_data, element_width, copy); + rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); continue; } const right_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width - current_block_len; - rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, right_len, cmp, cmp_data, element_width, copy); + rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); break; } } @@ -193,9 +201,15 @@ fn rotate_merge_block( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { var left_block = initial_left_block; var right = initial_right; + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } if (compare(cmp, cmp_data, array + (left_block - 1) * element_width, array + left_block * element_width) != GT) { // Lucky us, already sorted. return; @@ -204,7 +218,7 @@ fn rotate_merge_block( var right_block = left_block / 2; left_block -= right_block; - var left = monobound_binary_first(array + (left_block + right_block) * element_width, right, array + left_block * element_width, cmp, cmp_data, element_width); + var left = monobound_binary_first(array + (left_block + right_block) * element_width, right, array + left_block * element_width, cmp, cmp_data, element_width, data_is_owned, inc_n_data); right -= left; if (left != 0) { @@ -213,17 +227,17 @@ fn rotate_merge_block( @memcpy((swap + left_block * element_width)[0..(left * element_width)], (array + (left_block + right_block) * element_width)[0..(left * element_width)]); std.mem.copyBackwards(u8, (array + (left + left_block) * element_width)[0..(right_block * element_width)], (array + left_block * element_width)[0..(right_block * element_width)]); - cross_merge(array, swap, left_block, left, cmp, cmp_data, element_width, copy); + cross_merge(array, swap, left_block, left, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { trinity_rotation(array + left_block * element_width, right_block + left, swap, swap_len, right_block, element_width, copy); const unbalanced = (left * 2 < left_block) or (left_block * 2 < left); if (unbalanced and left <= swap_len) { - partial_backwards_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy); + partial_backwards_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else if (unbalanced and left_block <= swap_len) { - partial_forward_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy); + partial_forward_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { - rotate_merge_block(array, swap, swap_len, left_block, left, cmp, cmp_data, element_width, copy); + rotate_merge_block(array, swap, swap_len, left_block, left, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } } } @@ -231,11 +245,11 @@ fn rotate_merge_block( if (right != 0) { const unbalanced = (right * 2 < right_block) or (right_block * 2 < right); if ((unbalanced and right <= swap_len) or right + right_block <= swap_len) { - partial_backwards_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy); + partial_backwards_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else if (unbalanced and left_block <= swap_len) { - partial_forward_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy); + partial_forward_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { - rotate_merge_block(array + (left_block + left) * element_width, swap, swap_len, right_block, right, cmp, cmp_data, element_width, copy); + rotate_merge_block(array + (left_block + left) * element_width, swap, swap_len, right_block, right, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } } } @@ -248,10 +262,20 @@ fn monobound_binary_first( cmp: CompareFn, cmp_data: Opaque, element_width: usize, + comptime data_is_owned: bool, + inc_n_data: IncN, ) usize { var top = initial_top; var end_ptr = array + top * element_width; + if (data_is_owned) { + // We need to increment log2 of n times. + // We can get that by counting leading zeros and of (top - 1). + // Needs to be `-1` so values that are powers of 2 don't sort up a bin. + // Then just add 1 back to the final result. + const log2 = @bitSizeOf(usize) - @clz(top - 1) + 1; + inc_n_data(cmp_data, log2); + } while (top > 1) { const mid = top / 2; @@ -427,9 +451,9 @@ fn trinity_rotation( } } -// TODO: better testing for rotate_merge and rotate_merge_block (or just fuzzing). test "rotate_merge" { const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var test_count: i64 = 0; var arr: [10]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); @@ -437,52 +461,58 @@ test "rotate_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - rotate_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + rotate_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - rotate_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + rotate_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - rotate_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + rotate_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // Limited swap, can't finish merge arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - rotate_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + rotate_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); - - // TODO: should this work? Has too little swap. - // arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - // rotate_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - // try testing.expectEqual(arr, expected); } test "monobound_binary_first" { + var test_count: i64 = 0; + var arr = [25]i64{ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); var value: i64 = undefined; var value_ptr = @as([*]u8, @ptrCast(&value)); value = 7; - var res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + var res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 3); value = 39; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 19); value = 40; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 20); value = -10; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 0); value = 10000; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare, null, @sizeOf(i64)); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 25); } @@ -549,6 +579,8 @@ fn tail_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { const end_ptr = array + len * element_width; var current_block_len = block_len; @@ -556,11 +588,11 @@ fn tail_merge( var arr_ptr = array; while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += 2 * current_block_len * element_width) { if (@intFromPtr(arr_ptr) + 2 * current_block_len * element_width < @intFromPtr(end_ptr)) { - partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy); + partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); continue; } const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; - partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy); + partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); break; } } @@ -578,6 +610,8 @@ fn partial_backwards_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { std.debug.assert(swap_len >= block_len); @@ -589,6 +623,10 @@ fn partial_backwards_merge( var left_tail = array + (block_len - 1) * element_width; var dest_tail = array + (len - 1) * element_width; + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } if (compare(cmp, cmp_data, left_tail, left_tail + element_width) != GT) { // Lucky case, blocks happen to be sorted. return; @@ -598,7 +636,7 @@ fn partial_backwards_merge( if (len <= swap_len and right_len >= 64) { // Large remaining merge and we have enough space to just do it in swap. - cross_merge(swap, array, block_len, right_len, cmp, cmp_data, element_width, copy); + cross_merge(swap, array, block_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); @memcpy(array[0..(element_width * len)], swap[0..(element_width * len)]); @@ -611,7 +649,8 @@ fn partial_backwards_merge( // For backards, we first try to do really large chunks, of 16 elements. outer: while (@intFromPtr(left_tail) > @intFromPtr(array + 16 * element_width) and @intFromPtr(right_tail) > @intFromPtr(swap + 16 * element_width)) { - while (compare(cmp, cmp_data, left_tail, right_tail - 15 * element_width) != GT) { + // Due to if looping, these must use `compare_inc` + while (compare_inc(cmp, cmp_data, left_tail, right_tail - 15 * element_width, data_is_owned, inc_n_data) != GT) { inline for (0..16) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; @@ -620,7 +659,8 @@ fn partial_backwards_merge( if (@intFromPtr(right_tail) <= @intFromPtr(swap + 16 * element_width)) break :outer; } - while (compare(cmp, cmp_data, left_tail - 15 * element_width, right_tail) == GT) { + // Due to if looping, these must use `compare_inc` + while (compare_inc(cmp, cmp_data, left_tail - 15 * element_width, right_tail, data_is_owned, inc_n_data) == GT) { inline for (0..16) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -632,13 +672,14 @@ fn partial_backwards_merge( // Attempt to deal with the rest of the chunk in groups of 2. var loops: usize = 8; while (true) { - if (compare(cmp, cmp_data, left_tail, right_tail - element_width) != GT) { + // Due to if else chain and uncertain calling, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, left_tail, right_tail - element_width, data_is_owned, inc_n_data) != GT) { inline for (0..2) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; right_tail -= element_width; } - } else if (compare(cmp, cmp_data, left_tail - element_width, right_tail) == GT) { + } else if (compare_inc(cmp, cmp_data, left_tail - element_width, right_tail, data_is_owned, inc_n_data) == GT) { inline for (0..2) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -646,6 +687,10 @@ fn partial_backwards_merge( } } else { // Couldn't move two elements, do a cross swap and continue. + // 2 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 2); + } const lte = compare(cmp, cmp_data, left_tail, right_tail) != GT; var x = if (lte) element_width else 0; var not_x = if (!lte) element_width else 0; @@ -672,10 +717,14 @@ fn partial_backwards_merge( // The C use `goto` to implement the two tail recursive functions below inline. // I think the closest equivalent in zig would be to use an enum and a switch. // That would potentially optimize to computed gotos. - const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp, cmp_data, element_width, copy); + const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); if (break_loop) break; + // 2 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 2); + } // Couldn't move two elements, do a cross swap and continue. const lte = compare(cmp, cmp_data, left_tail, right_tail) != GT; var x = if (lte) element_width else 0; @@ -692,6 +741,11 @@ fn partial_backwards_merge( // Deal with tail. while (@intFromPtr(right_tail) >= @intFromPtr(swap) and @intFromPtr(left_tail) >= @intFromPtr(array)) { + // This feels like a place where we may be able reduce inc_n_data calls. + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); } while (@intFromPtr(right_tail) >= @intFromPtr(swap)) { @@ -714,26 +768,28 @@ fn partial_forward_merge_right_tail_2( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) bool { - if (compare(cmp, cmp_data, left_tail.*, right_tail.* - element_width) != GT) { + if (compare_inc(cmp, cmp_data, left_tail.*, right_tail.* - element_width, data_is_owned, inc_n_data) != GT) { inline for (0..2) |_| { copy(dest.*, right_tail.*); dest.* -= element_width; right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } - if (compare(cmp, cmp_data, left_tail.* - element_width, right_tail.*) == GT) { + if (compare_inc(cmp, cmp_data, left_tail.* - element_width, right_tail.*, data_is_owned, inc_n_data) == GT) { inline for (0..2) |_| { copy(dest.*, left_tail.*); dest.* -= element_width; left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } @@ -750,26 +806,28 @@ fn partial_forward_merge_left_tail_2( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) bool { - if (compare(cmp, cmp_data, left_tail.* - element_width, right_tail.*) == GT) { + if (compare_inc(cmp, cmp_data, left_tail.* - element_width, right_tail.*, data_is_owned, inc_n_data) == GT) { inline for (0..2) |_| { copy(dest.*, left_tail.*); dest.* -= element_width; left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } - if (compare(cmp, cmp_data, left_tail.*, right_tail.* - element_width) != GT) { + if (compare_inc(cmp, cmp_data, left_tail.*, right_tail.* - element_width, data_is_owned, inc_n_data) != GT) { inline for (0..2) |_| { copy(dest.*, right_tail.*); dest.* -= element_width; right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } @@ -788,6 +846,8 @@ fn partial_forward_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { std.debug.assert(swap_len >= block_len); @@ -799,6 +859,10 @@ fn partial_forward_merge( var right_head = array + block_len * element_width; var right_tail = array + (len - 1) * element_width; + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } if (compare(cmp, cmp_data, right_head - element_width, right_head) != GT) { // Lucky case, blocks happen to be sorted. return; @@ -817,10 +881,14 @@ fn partial_forward_merge( // The C use `goto` to implement the two tail recursive functions below inline. // I think the closest equivalent in zig would be to use an enum and a switch. // That would potentially optimize to computed gotos. - const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp, cmp_data, element_width, copy); + const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); if (break_loop) break; + // 2 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 2); + } // Couldn't move two elements, do a cross swap and continue. const lte = compare(cmp, cmp_data, left_head, right_head) != GT; var x = if (lte) element_width else 0; @@ -836,6 +904,11 @@ fn partial_forward_merge( // Deal with tail. while (@intFromPtr(left_head) <= @intFromPtr(left_tail) and @intFromPtr(right_head) <= @intFromPtr(right_tail)) { + // This feels like a place where we may be able reduce inc_n_data calls. + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { @@ -858,26 +931,28 @@ fn partial_forward_merge_right_head_2( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) bool { - if (compare(cmp, cmp_data, left_head.*, right_head.* + element_width) == GT) { + if (compare_inc(cmp, cmp_data, left_head.*, right_head.* + element_width, data_is_owned, inc_n_data) == GT) { inline for (0..2) |_| { copy(dest.*, right_head.*); dest.* += element_width; right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } - if (compare(cmp, cmp_data, left_head.* + element_width, right_head.*) != GT) { + if (compare_inc(cmp, cmp_data, left_head.* + element_width, right_head.*, data_is_owned, inc_n_data) != GT) { inline for (0..2) |_| { copy(dest.*, left_head.*); dest.* += element_width; left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } @@ -894,26 +969,28 @@ fn partial_forward_merge_left_head_2( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) bool { - if (compare(cmp, cmp_data, left_head.* + element_width, right_head.*) != GT) { + if (compare_inc(cmp, cmp_data, left_head.* + element_width, right_head.*, data_is_owned, inc_n_data) != GT) { inline for (0..2) |_| { copy(dest.*, left_head.*); dest.* += element_width; left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } - if (compare(cmp, cmp_data, left_head.*, right_head.* + element_width) == GT) { + if (compare_inc(cmp, cmp_data, left_head.*, right_head.* + element_width, data_is_owned, inc_n_data) == GT) { inline for (0..2) |_| { copy(dest.*, right_head.*); dest.* += element_width; right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy); + return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return true; } @@ -921,6 +998,7 @@ fn partial_forward_merge_left_head_2( } test "tail_merge" { + var test_count: i64 = 0; const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var arr: [10]i64 = undefined; @@ -929,19 +1007,23 @@ test "tail_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } test "partial_backwards_merge" { + var test_count: i64 = 0; { const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; @@ -951,19 +1033,23 @@ test "partial_backwards_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -991,7 +1077,8 @@ test "partial_backwards_merge" { for (0..16) |i| { arr[i + 48] = @intCast(i + 33); } - partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // chunks with break @@ -1010,12 +1097,14 @@ test "partial_backwards_merge" { arr[16] = 33; arr[63] = 49; - partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } } test "partial_forward_merge" { + var test_count: i64 = 0; const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var arr: [10]i64 = undefined; @@ -1024,19 +1113,23 @@ test "partial_forward_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -1055,6 +1148,8 @@ fn quad_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) usize { const end_ptr = array + len * element_width; var current_block_len = block_len * 4; @@ -1062,7 +1157,7 @@ fn quad_merge( while (current_block_len <= len and current_block_len <= swap_len) : (current_block_len *= 4) { var arr_ptr = array; while (true) { - quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp, cmp_data, element_width, copy); + quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); arr_ptr += current_block_len * element_width; if (@intFromPtr(arr_ptr) + current_block_len * element_width > @intFromPtr(end_ptr)) @@ -1070,10 +1165,10 @@ fn quad_merge( } const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; - tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy); + tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } - tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy); + tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return current_block_len / 2; } @@ -1087,6 +1182,8 @@ fn quad_merge_block( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { const block_x_2 = 2 * block_len; @@ -1095,23 +1192,27 @@ fn quad_merge_block( const block3 = block2 + block_len * element_width; const block4 = block3 + block_len * element_width; + // 2 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 2); + } const in_order_1_2: u2 = @intFromBool(compare(cmp, cmp_data, block2 - element_width, block2) != GT); const in_order_3_4: u2 = @intFromBool(compare(cmp, cmp_data, block4 - element_width, block4) != GT); switch (in_order_1_2 | (in_order_3_4 << 1)) { 0 => { // Nothing sorted. Just run merges on both. - cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy); - cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy); + cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 1 => { // First half sorted already. @memcpy(swap[0..(element_width * block_x_2)], array[0..(element_width * block_x_2)]); - cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 2 => { // Second half sorted already. - cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy); + cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); @memcpy((swap + element_width * block_x_2)[0..(element_width * block_x_2)], block3[0..(element_width * block_x_2)]); }, 3 => { @@ -1126,7 +1227,7 @@ fn quad_merge_block( } // Merge 2 larger blocks. - cross_merge(array, swap, block_x_2, block_x_2, cmp, cmp_data, element_width, copy); + cross_merge(array, swap, block_x_2, block_x_2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } /// Cross merge attempts to merge two arrays in chunks of multiple elements. @@ -1139,6 +1240,8 @@ fn cross_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { var left_head = src; var right_head = src + left_len * element_width; @@ -1149,8 +1252,9 @@ fn cross_merge( // fallback to the branchless parity merge. if (left_len + 1 >= right_len and right_len + 1 >= left_len and left_len >= 32) { const offset = 15 * element_width; - if (compare(cmp, cmp_data, left_head + offset, right_head) == GT and compare(cmp, cmp_data, left_head, right_head + offset) != GT and compare(cmp, cmp_data, left_tail, right_tail - offset) == GT and compare(cmp, cmp_data, left_tail - offset, right_tail) != GT) { - parity_merge(dest, src, left_len, right_len, cmp, cmp_data, element_width, copy); + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, left_head + offset, right_head, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, left_head, right_head + offset, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, left_tail, right_tail - offset, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, left_tail - offset, right_tail, data_is_owned, inc_n_data) != GT) { + parity_merge(dest, src, left_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return; } } @@ -1162,7 +1266,8 @@ fn cross_merge( // This has to be allowed to go negative to be correct. Thus, isize. if (@as(isize, @intCast(@intFromPtr(left_tail))) - @as(isize, @intCast(@intFromPtr(left_head))) > @as(isize, @intCast(8 * element_width))) { // 8 elements all less than or equal to and can be moved together. - while (compare(cmp, cmp_data, left_head + 7 * element_width, right_head) != GT) { + // Due to looping, these must use `compare_inc` + while (compare_inc(cmp, cmp_data, left_head + 7 * element_width, right_head, data_is_owned, inc_n_data) != GT) { inline for (0..8) |_| { copy(dest_head, left_head); dest_head += element_width; @@ -1174,7 +1279,8 @@ fn cross_merge( // Attempt to do the same from the tail. // 8 elements all greater than and can be moved together. - while (compare(cmp, cmp_data, left_tail - 7 * element_width, right_tail) == GT) { + // Due to looping, these must use `compare_inc` + while (compare_inc(cmp, cmp_data, left_tail - 7 * element_width, right_tail, data_is_owned, inc_n_data) == GT) { inline for (0..8) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -1189,7 +1295,8 @@ fn cross_merge( // This has to be allowed to go negative to be correct. Thus, isize. if (@as(isize, @intCast(@intFromPtr(right_tail))) - @as(isize, @intCast(@intFromPtr(right_head))) > @as(isize, @intCast(8 * element_width))) { // left greater than 8 elements right and can be moved together. - while (compare(cmp, cmp_data, left_head, right_head + 7 * element_width) == GT) { + // Due to looping, these must use `compare_inc` + while (compare_inc(cmp, cmp_data, left_head, right_head + 7 * element_width, data_is_owned, inc_n_data) == GT) { inline for (0..8) |_| { copy(dest_head, right_head); dest_head += element_width; @@ -1201,7 +1308,8 @@ fn cross_merge( // Attempt to do the same from the tail. // left less than or equalt to 8 elements right and can be moved together. - while (compare(cmp, cmp_data, left_tail, right_tail - 7 * element_width) != GT) { + // Due to looping, these must use `compare_inc` + while (compare_inc(cmp, cmp_data, left_tail, right_tail - 7 * element_width, data_is_owned, inc_n_data) != GT) { inline for (0..8) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; @@ -1216,6 +1324,10 @@ fn cross_merge( break; // Large enough to warrent a two way merge. + // 16 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 16); + } for (0..8) |_| { head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); @@ -1224,6 +1336,11 @@ fn cross_merge( // Clean up tail. while (@intFromPtr(left_head) <= @intFromPtr(left_tail) and @intFromPtr(right_head) <= @intFromPtr(right_tail)) { + // This feels like a place where we may be able reduce inc_n_data calls. + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { @@ -1239,6 +1356,7 @@ fn cross_merge( } test "quad_merge" { + var test_count: i64 = 0; const expected = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var arr: [10]i64 = undefined; @@ -1248,33 +1366,39 @@ test "quad_merge" { var size: usize = undefined; arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 16); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 16); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 8); // Limited swap, can't finish merge arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [10]i64{ 1, 3, 4, 5, 6, 7, 8, 9, 2, 10 }); try testing.expectEqual(size, 4); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [10]i64{ 5, 6, 7, 8, 1, 3, 4, 9, 2, 10 }); try testing.expectEqual(size, 4); } test "quad_merge_block" { + var test_count: i64 = 0; const expected = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; var arr: [8]i64 = undefined; @@ -1284,31 +1408,38 @@ test "quad_merge_block" { // case 0 - totally unsorted arr = [8]i64{ 7, 8, 5, 6, 3, 4, 1, 2 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 1 - first half sorted arr = [8]i64{ 5, 6, 7, 8, 3, 4, 1, 2 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 2 - second half sorted arr = [8]i64{ 7, 8, 5, 6, 1, 2, 3, 4 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 3 both haves sorted arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + // TODO: fix + // try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 3 - lucky, sorted arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + // try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } test "cross_merge" { + var test_count: i64 = 0; var expected: [64]i64 = undefined; for (0..64) |i| { expected[i] = @intCast(i + 1); @@ -1326,7 +1457,8 @@ test "cross_merge" { for (0..32) |i| { src[i + 32] = @intCast(i + 1); } - cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); // will fallback, every other @@ -1334,7 +1466,8 @@ test "cross_merge" { src[i * 2] = @intCast(i * 2 + 1); src[i * 2 + 1] = @intCast(i * 2 + 2); } - cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); // super uneven @@ -1344,7 +1477,8 @@ test "cross_merge" { for (0..44) |i| { src[i + 20] = @intCast(i + 1); } - cross_merge(dest_ptr, src_ptr, 20, 44, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 20, 44, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); // chunks @@ -1360,7 +1494,8 @@ test "cross_merge" { for (0..16) |i| { src[i + 48] = @intCast(i + 33); } - cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); } @@ -1379,6 +1514,8 @@ fn quad_swap( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) QuadSwapResult { // TODO: This is a solid amount of stack space. Is that ok? // That said, it only ever allocates once (not recursive). @@ -1397,6 +1534,10 @@ fn quad_swap( outer: while (count != 0) { count -= 1; + // 4 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 4); + } var v1: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) == GT); var v2: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) == GT); var v3: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) == GT); @@ -1409,9 +1550,14 @@ fn quad_swap( switch (v1 | (v2 << 1) | (v3 << 2) | (v4 << 3)) { 0 => { // potentially already ordered, check rest! - if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) { break :switch_state .ordered; } + // 16 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 16); + } quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); arr_ptr += 8 * element_width; @@ -1419,7 +1565,8 @@ fn quad_swap( }, 15 => { // potentially already reverse ordered, check rest! - if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { reverse_head = arr_ptr; break :switch_state .reversed; } @@ -1443,6 +1590,10 @@ fn quad_swap( } arr_ptr -= 8 * element_width; + // 16 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 16); + } quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); arr_ptr += 8 * element_width; @@ -1454,13 +1605,18 @@ fn quad_swap( // 1 group was order, lets see if that continues! if (count != 0) { count -= 1; + // 4 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 4); + } v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) == GT); v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) == GT); v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) == GT); v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) == GT); if (v1 | v2 | v3 | v4 != 0) { // Sadly not ordered still, maybe reversed though? - if (v1 + v2 + v3 + v4 == 4 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // Due to short circuit logic, these must use `compare_inc` + if (v1 + v2 + v3 + v4 == 4 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { reverse_head = arr_ptr; state = .reversed; continue; @@ -1468,11 +1624,16 @@ fn quad_swap( state = .not_ordered; continue; } - if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) { state = .ordered; continue; } + // 16 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 16); + } quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); arr_ptr += 8 * element_width; continue :outer; @@ -1485,6 +1646,10 @@ fn quad_swap( // 1 group was reversed, lets see if that continues! if (count != 0) { count -= 1; + // 4 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 4); + } v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) != GT); v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT); v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) != GT); @@ -1494,7 +1659,8 @@ fn quad_swap( // So we just need to reverse upto this point, but not the current 8 element block. } else { // This also checks the boundary between this and the last block. - if (compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { // Row multiple reversed blocks in a row! state = .reversed; continue; @@ -1504,11 +1670,13 @@ fn quad_swap( quad_reversal(reverse_head, arr_ptr - element_width, element_width, copy); // Since we already have v1 to v4, check the next block state. - if (v1 + v2 + v3 + v4 == 4 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) { + // Due to short circuit logic, these must use `compare_inc` + if (v1 + v2 + v3 + v4 == 4 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) { state = .ordered; continue; } - if (v1 + v2 + v3 + v4 == 0 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // Due to short circuit logic, these must use `compare_inc` + if (v1 + v2 + v3 + v4 == 0 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { reverse_head = arr_ptr; state = .reversed; continue; @@ -1525,7 +1693,12 @@ fn quad_swap( } arr_ptr -= 8 * element_width; - if (compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) == GT or compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) == GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT or compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT or compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { + // 16 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 16); + } quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); } arr_ptr += 8 * element_width; @@ -1535,19 +1708,20 @@ fn quad_swap( // Handle tail block when reversing. const rem = len % 8; reverse_block: { - if (rem == 7 and compare(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width) != GT) + // Due to chance of breaking and not running, must use `comapare_inc`. + if (rem == 7 and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; - if (rem >= 6 and compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) != GT) + if (rem >= 6 and compare_inc(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; - if (rem >= 5 and compare(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width) != GT) + if (rem >= 5 and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; - if (rem >= 4 and compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT) + if (rem >= 4 and compare_inc(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; - if (rem >= 3 and compare(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width) != GT) + if (rem >= 3 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; - if (rem >= 2 and compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) != GT) + if (rem >= 2 and compare_inc(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; - if (rem >= 1 and compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width) != GT) + if (rem >= 1 and compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width, data_is_owned, inc_n_data) != GT) break :reverse_block; quad_reversal(reverse_head, arr_ptr + rem * element_width - element_width, element_width, copy); @@ -1566,7 +1740,7 @@ fn quad_swap( } } if (!skip_tail_swap) { - tail_swap(arr_ptr, len % 8, swap, cmp, cmp_data, element_width, copy); + tail_swap(arr_ptr, len % 8, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } // Group into 32 element blocks. @@ -1577,25 +1751,27 @@ fn quad_swap( count -= 1; arr_ptr += 32 * element_width; }) { - if (compare(cmp, cmp_data, arr_ptr + 7 * element_width, arr_ptr + 8 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 15 * element_width, arr_ptr + 16 * element_width) != GT and compare(cmp, cmp_data, arr_ptr + 23 * element_width, arr_ptr + 24 * element_width) != GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, arr_ptr + 7 * element_width, arr_ptr + 8 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 15 * element_width, arr_ptr + 16 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 23 * element_width, arr_ptr + 24 * element_width, data_is_owned, inc_n_data) != GT) { // Already in order. continue; } - parity_merge(swap, arr_ptr, 8, 8, cmp, cmp_data, element_width, copy); - parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp, cmp_data, element_width, copy); - parity_merge(arr_ptr, swap, 16, 16, cmp, cmp_data, element_width, copy); + parity_merge(swap, arr_ptr, 8, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_merge(arr_ptr, swap, 16, 16, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } // Deal with final tail for 32 element blocks. // Anything over 8 elements is multiple blocks worth merging together. if (len % 32 > 8) { - tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp, cmp_data, element_width, copy); + tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } return .unfinished; } /// Merge 4 sorted arrays of length 2 into a sorted array of length 8 using swap space. +/// Requires that the refcount of cmp_data be incremented 16 times. fn quad_swap_merge( array: [*]u8, swap: [*]u8, @@ -1661,6 +1837,7 @@ fn quad_reversal( } test "quad_swap" { + var test_count: i64 = 0; var arr: [75]i64 = undefined; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); @@ -1687,7 +1864,8 @@ test "quad_swap" { 72, 58, 57, }; - var result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + var result = quad_swap(arr_ptr, 75, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(result, .unfinished); try testing.expectEqual(arr, [75]i64{ // first 32 elements sorted (with 8 reversed that get flipped here) @@ -1718,7 +1896,8 @@ test "quad_swap" { expected[i] = @intCast(i + 1); arr[i] = @intCast(75 - i); } - result = quad_swap(arr_ptr, 75, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + result = quad_swap(arr_ptr, 75, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(result, .sorted); try testing.expectEqual(arr, expected); } @@ -1775,11 +1954,13 @@ fn tail_swap( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { std.debug.assert(len < 32); if (len < 8) { - tiny_sort(array, len, swap, cmp, cmp_data, element_width, copy); + tiny_sort(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return; } @@ -1791,21 +1972,22 @@ fn tail_swap( const quad4 = half2 - quad3; var arr_ptr = array; - tail_swap(arr_ptr, quad1, swap, cmp, cmp_data, element_width, copy); + tail_swap(arr_ptr, quad1, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); arr_ptr += quad1 * element_width; - tail_swap(arr_ptr, quad2, swap, cmp, cmp_data, element_width, copy); + tail_swap(arr_ptr, quad2, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); arr_ptr += quad2 * element_width; - tail_swap(arr_ptr, quad3, swap, cmp, cmp_data, element_width, copy); + tail_swap(arr_ptr, quad3, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); arr_ptr += quad3 * element_width; - tail_swap(arr_ptr, quad4, swap, cmp, cmp_data, element_width, copy); + tail_swap(arr_ptr, quad4, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - if (compare(cmp, cmp_data, array + (quad1 - 1) * element_width, array + quad1 * element_width) != GT and compare(cmp, cmp_data, array + (half1 - 1) * element_width, array + half1 * element_width) != GT and compare(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr) != GT) { + // Due to short circuit logic, these must use `compare_inc` + if (compare_inc(cmp, cmp_data, array + (quad1 - 1) * element_width, array + quad1 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, array + (half1 - 1) * element_width, array + half1 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr, data_is_owned, inc_n_data) != GT) { return; } - parity_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy); - parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy); - parity_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy); + parity_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } /// Merges two neighboring sorted arrays into dest. @@ -1819,6 +2001,8 @@ fn parity_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { std.debug.assert(left_len == right_len or left_len == right_len - 1 or left_len - 1 == right_len); @@ -1831,8 +2015,17 @@ fn parity_merge( var dest_tail = dest + (left_len + right_len - 1) * element_width; if (left_len < right_len) { + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); } + + // 2 + 2(left_len -1) = (2*left_len) guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 2 * left_len); + } head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); for (0..(left_len - 1)) |_| { @@ -1843,6 +2036,7 @@ fn parity_merge( } test "tail_swap" { + var test_count: i64 = 0; var swap: [31]i64 = undefined; var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); @@ -1858,12 +2052,14 @@ test "tail_swap" { var rng = std.rand.DefaultPrng.init(seed); rng.random().shuffle(i64, arr[0..]); - tail_swap(arr_ptr, 31, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_swap(arr_ptr, 31, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } } test "parity_merge" { + var test_count: i64 = 0; { var dest: [8]i64 = undefined; var dest_ptr = @as([*]u8, @ptrCast(&dest[0])); @@ -1873,12 +2069,14 @@ test "parity_merge" { arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } { @@ -1890,12 +2088,26 @@ test "parity_merge" { arr = [9]i64{ 1, 3, 5, 8, 2, 4, 6, 7, 9 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); arr = [9]i64{ 6, 7, 8, 9, 1, 2, 3, 4, 5 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + + arr = [9]i64{ 1, 3, 5, 7, 8, 2, 4, 6, 9 }; + dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge(dest_ptr, arr_ptr, 5, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + + arr = [9]i64{ 5, 6, 7, 8, 9, 1, 2, 3, 4 }; + dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + parity_merge(dest_ptr, arr_ptr, 5, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); } } @@ -1912,6 +2124,8 @@ fn tiny_sort( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { std.debug.assert(len < 8); @@ -1923,9 +2137,17 @@ fn tiny_sort( return; }, 2 => { + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } swap_branchless(array, tmp_ptr, cmp, cmp_data, element_width, copy); }, 3 => { + // 3 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 3); + } var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += element_width; @@ -1934,16 +2156,16 @@ fn tiny_sort( swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); }, 4 => { - parity_swap_four(array, tmp_ptr, cmp, cmp_data, element_width, copy); + parity_swap_four(array, tmp_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 5 => { - parity_swap_five(array, tmp_ptr, cmp, cmp_data, element_width, copy); + parity_swap_five(array, tmp_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 6 => { - parity_swap_six(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy); + parity_swap_six(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 7 => { - parity_swap_seven(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy); + parity_swap_seven(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, else => { unreachable; @@ -1958,7 +2180,13 @@ fn parity_swap_four( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { + // 3 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 3); + } var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; @@ -1967,6 +2195,10 @@ fn parity_swap_four( const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; if (gt) { + // 3 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 3); + } copy(tmp_ptr, arr_ptr); copy(arr_ptr, arr_ptr + element_width); copy(arr_ptr + element_width, tmp_ptr); @@ -1986,7 +2218,13 @@ fn parity_swap_five( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { + // 4 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 4); + } var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; @@ -1998,6 +2236,10 @@ fn parity_swap_five( arr_ptr = array; if (more_work != 0) { + // 6 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 6); + } swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); @@ -2020,7 +2262,13 @@ fn parity_swap_six( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { + // 7 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 5); + } var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += element_width; @@ -2034,6 +2282,10 @@ fn parity_swap_six( { const lte = compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT; if (lte) { + // 2 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 2); + } swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 4 * element_width; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); @@ -2041,6 +2293,10 @@ fn parity_swap_six( } } + // 8 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 8); + } { const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; var x = if (gt) element_width else 0; @@ -2086,7 +2342,13 @@ fn parity_swap_seven( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, ) void { + // 6 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 6); + } var arr_ptr = array; swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr += 2 * element_width; @@ -2104,6 +2366,10 @@ fn parity_swap_seven( if (more_work == 0) return; + // 11 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 11); + } swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); arr_ptr = array; @@ -2153,6 +2419,7 @@ fn parity_swap_seven( } test "tiny_sort" { + var test_count: i64 = 0; var swap: [7]i64 = undefined; var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); @@ -2161,11 +2428,13 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [7]i64{ 3, 1, 2, 5, 4, 7, 6 }; - tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); arr = [7]i64{ 7, 6, 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); } { @@ -2173,11 +2442,13 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [6]i64{ 3, 1, 2, 6, 4, 5 }; - tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); arr = [6]i64{ 6, 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); } { @@ -2185,11 +2456,13 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [5]i64{ 2, 1, 4, 3, 5 }; - tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); arr = [5]i64{ 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); } { @@ -2197,23 +2470,27 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [4]i64{ 4, 2, 1, 3 }; - tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 1, 4, 3 }; - tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); } { var arr = [3]i64{ 2, 3, 1 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - tiny_sort(arr_ptr, 3, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 3, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [3]i64{ 1, 2, 3 }); } { var arr = [2]i64{ 2, 1 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - tiny_sort(arr_ptr, 2, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tiny_sort(arr_ptr, 2, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [2]i64{ 1, 2 }); } } @@ -2224,6 +2501,7 @@ test "tiny_sort" { // The are the smallest fundamental unit. /// Merge two neighboring sorted 4 element arrays into dest. +/// Requires that the refcount of cmp_data be incremented 8 times. inline fn parity_merge_four( dest: [*]u8, array: [*]u8, @@ -2254,6 +2532,7 @@ inline fn parity_merge_four( } /// Merge two neighboring sorted 2 element arrays into dest. +/// Requires that the refcount of cmp_data be incremented 4 times. inline fn parity_merge_two( dest: [*]u8, array: [*]u8, @@ -2283,6 +2562,7 @@ inline fn parity_merge_two( /// Will increment both dest and the smaller element ptr to their next index. /// Inlining will remove the extra level of pointer indirection here. /// It is just used to allow mutating the input pointers. +/// Requires that the refcount of cmp_data be incremented 1 time. inline fn head_branchless_merge( dest: *[*]u8, left: *[*]u8, @@ -2306,6 +2586,7 @@ inline fn head_branchless_merge( /// Will decrement both dest and the smaller element ptr to their previous index. /// Inlining will remove the extra level of pointer indirection here. /// It is just used to allow mutating the input pointers. +/// Requires that the refcount of cmp_data be incremented 1 time. inline fn tail_branchless_merge( dest: *[*]u8, left: *[*]u8, @@ -2326,6 +2607,7 @@ inline fn tail_branchless_merge( } /// Swaps the element at ptr with the element after it if the element is greater than the next. +/// Requires that the refcount of cmp_data be incremented 1 time. inline fn swap_branchless( ptr: [*]u8, tmp: [*]u8, @@ -2338,6 +2620,7 @@ inline fn swap_branchless( _ = swap_branchless_return_gt(ptr, tmp, cmp, cmp_data, element_width, copy); } +/// Requires that the refcount of cmp_data be incremented 1 time. inline fn swap_branchless_return_gt( ptr: [*]u8, tmp: [*]u8, @@ -2356,10 +2639,29 @@ inline fn swap_branchless_return_gt( return @intFromBool(gt); } +/// Requires that the refcount of cmp_data be incremented 1 time. inline fn compare(cmp: CompareFn, cmp_data: Opaque, lhs: [*]u8, rhs: [*]u8) Ordering { return @as(Ordering, @enumFromInt(cmp(cmp_data, lhs, rhs))); } +/// Only use this as a last resort. +/// It will increment the refcount before comparing. +/// Incrementing for each individual compare is slow. +/// Perfer to increment in batches where possible. +inline fn compare_inc( + cmp: CompareFn, + cmp_data: Opaque, + lhs: [*]u8, + rhs: [*]u8, + comptime data_is_owned: bool, + inc_n_data: IncN, +) Ordering { + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } + return compare(cmp, cmp_data, lhs, rhs); +} + test "parity_merge_four" { var arr: [8]i64 = undefined; var dest: [8]i64 = undefined; @@ -2494,6 +2796,24 @@ fn test_i64_compare(_: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { return lt + lt + gt; } +fn test_i64_compare_refcounted(count_ptr: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { + const a = @as(*i64, @alignCast(@ptrCast(a_ptr))).*; + const b = @as(*i64, @alignCast(@ptrCast(b_ptr))).*; + + const gt = @as(u8, @intFromBool(a > b)); + const lt = @as(u8, @intFromBool(a < b)); + + @as(*isize, @ptrCast(@alignCast(count_ptr))).* -= @intCast(1); + // Eq = 0 + // GT = 1 + // LT = 2 + return lt + lt + gt; +} + fn test_i64_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { @as(*i64, @alignCast(@ptrCast(dst_ptr))).* = @as(*i64, @alignCast(@ptrCast(src_ptr))).*; } + +fn test_inc_n_data(count_ptr: Opaque, n: usize) callconv(.C) void { + @as(*isize, @ptrCast(@alignCast(count_ptr))).* += @intCast(n); +} From c9a47ae886051382767720a7acb5f7b8e885a3b4 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 17:26:12 -0700 Subject: [PATCH 108/203] add refcounting to fuzzing and fix first fuzzing bug --- .../compiler/builtins/bitcode/src/fuzz_sort.zig | 16 +++++++++++----- crates/compiler/builtins/bitcode/src/sort.zig | 6 +++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index 833d21941c5..14b5e6d05d9 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -4,8 +4,9 @@ const sort = @import("sort.zig"); extern fn malloc(size: usize) callconv(.C) ?*anyopaque; extern fn free(c_ptr: *anyopaque) callconv(.C) void; -fn cMain() callconv(.C) void { +fn cMain() callconv(.C) i32 { fuzz_main() catch unreachable; + return 0; } comptime { @@ -35,23 +36,26 @@ pub fn fuzz_main() !void { std.debug.print("Input: [{d}]{d}\n", .{ size, arr_ptr[0..size] }); } - sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare, null, false, &test_i64_inc_n, @sizeOf(i64), @alignOf(i64), &test_i64_copy); + var test_count: i64 = 0; + sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare_refcounted, @ptrCast(&test_count), true, &test_inc_n_data, @sizeOf(i64), @alignOf(i64), &test_i64_copy); const sorted = std.sort.isSorted(i64, arr_ptr[0..size], {}, std.sort.asc(i64)); if (DEBUG) { - std.debug.print("Output: [{d}]{d}\nSorted: {}\n", .{ size, arr_ptr[0..size], sorted }); + std.debug.print("Output: [{d}]{d}\nSorted: {}\nFinal RC: {}\n", .{ size, arr_ptr[0..size], sorted, test_count }); } std.debug.assert(sorted); + std.debug.assert(test_count == 0); } const Opaque = ?[*]u8; -fn test_i64_compare(_: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { +fn test_i64_compare_refcounted(count_ptr: Opaque, a_ptr: Opaque, b_ptr: Opaque) callconv(.C) u8 { const a = @as(*i64, @alignCast(@ptrCast(a_ptr))).*; const b = @as(*i64, @alignCast(@ptrCast(b_ptr))).*; const gt = @as(u8, @intFromBool(a > b)); const lt = @as(u8, @intFromBool(a < b)); + @as(*isize, @ptrCast(@alignCast(count_ptr))).* -= 1; // Eq = 0 // GT = 1 // LT = 2 @@ -62,7 +66,9 @@ fn test_i64_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { @as(*i64, @alignCast(@ptrCast(dst_ptr))).* = @as(*i64, @alignCast(@ptrCast(src_ptr))).*; } -fn test_i64_inc_n(_: ?[*]u8, _: usize) callconv(.C) void {} +fn test_inc_n_data(count_ptr: Opaque, n: usize) callconv(.C) void { + @as(*isize, @ptrCast(@alignCast(count_ptr))).* += @intCast(n); +} comptime { @export(testing_roc_alloc, .{ .name = "roc_alloc", .linkage = .Strong }); diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index e84e6877ad8..2f7135752a6 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -1216,6 +1216,10 @@ fn quad_merge_block( @memcpy((swap + element_width * block_x_2)[0..(element_width * block_x_2)], block3[0..(element_width * block_x_2)]); }, 3 => { + // 1 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 1); + } const in_order_2_3 = compare(cmp, cmp_data, block3 - element_width, block3) != GT; if (in_order_2_3) // Lucky, all sorted. @@ -2803,7 +2807,7 @@ fn test_i64_compare_refcounted(count_ptr: Opaque, a_ptr: Opaque, b_ptr: Opaque) const gt = @as(u8, @intFromBool(a > b)); const lt = @as(u8, @intFromBool(a < b)); - @as(*isize, @ptrCast(@alignCast(count_ptr))).* -= @intCast(1); + @as(*isize, @ptrCast(@alignCast(count_ptr))).* -= 1; // Eq = 0 // GT = 1 // LT = 2 From e722faaf58575ca922cf5868fa17d59de20d40f7 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 27 Jul 2024 23:00:48 -0700 Subject: [PATCH 109/203] add fluxsort --- crates/compiler/builtins/bitcode/src/list.zig | 2 +- crates/compiler/builtins/bitcode/src/sort.zig | 807 +++++++++++++++++- 2 files changed, 802 insertions(+), 7 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index 5f198815f87..e89b39c5611 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -710,7 +710,7 @@ pub fn listSortWith( var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec); if (list.bytes) |source_ptr| { - sort.quadsort(source_ptr, list.len(), cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); + sort.fluxsort(source_ptr, list.len(), cmp, cmp_data, data_is_owned, inc_n_data, element_width, alignment, copy); } return list; diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 2f7135752a6..e09ac5bd532 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -29,8 +29,803 @@ comptime { std.debug.assert(MAX_ELEMENT_BUFFER_SIZE % BufferAlign == 0); } +// ================ Fluxsort ================================================== +// The high level fluxsort functions. + +pub fn fluxsort( + array: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + data_is_owned_runtime: bool, + inc_n_data: IncN, + element_width: usize, + alignment: u32, + copy: CopyFn, +) void { + // Note, knowing constant versions of element_width and copy could have huge perf gains. + // Hopefully llvm will essentially always do it via constant argument propagation and inlining. + // If not, we may want to generate `n` different version of this function with comptime. + // Then have our builtin dispatch to the correct version. + // llvm garbage collection would remove all other variants. + // Also, for numeric types, inlining the compare function can be a 2x perf gain. + if (len < 132) { + // Just quadsort it. + quadsort(array, len, cmp, cmp_data, data_is_owned_runtime, inc_n_data, element_width, alignment, copy); + } + if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { + if (data_is_owned_runtime) { + fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); + } else { + fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data); + } + } else { + if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { + // Build list of pointers to sort. + var arr_ptr = @as([*]Opaque, @ptrCast(@alignCast(alloc_ptr))); + defer utils.dealloc(alloc_ptr, @alignOf(usize)); + for (0..len) |i| { + arr_ptr[i] = array + i * element_width; + } + + // Setup for indirect comparison. + inner_cmp = cmp; + defer inner_cmp = null; + + // Sort. + if (data_is_owned_runtime) { + fluxsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, true, inc_n_data); + } else { + fluxsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, false, inc_n_data); + } + + if (utils.alloc(len * element_width, alignment)) |collect_ptr| { + // Collect sorted pointers into correct order. + defer utils.dealloc(collect_ptr, alignment); + for (0..len) |i| { + copy(collect_ptr + i * element_width, arr_ptr[i]); + } + + // Copy to original array as sorted. + @memcpy(array[0..(len * element_width)], collect_ptr[0..(len * element_width)]); + } else { + roc_panic("Out of memory while trying to allocate for sorting", 0); + } + } else { + roc_panic("Out of memory while trying to allocate for sorting", 0); + } + } +} + +fn fluxsort_direct( + array: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + alignment: u32, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) void { + if (utils.alloc(len * element_width, alignment)) |swap| { + flux_analyze(array, len, swap, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + + utils.dealloc(swap, alignment); + } else { + // Fallback to quadsort. It has ways to use less memory. + quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, data_is_owned, inc_n_data); + } +} + +/// This value is used to help stay within l3 cache when sorting. +/// It technically should be tuned based on l3 cache size. +/// This is important for large arrays with pointers to other data. +/// 262144 is tude for a 6MB L3 cache. +/// For primitives and other small inline values, making this essentially infinite is better. +const QUAD_CACHE = 262144; + +// When to stop using flux partition and switch to quadsort. +const FLUX_OUT = 96; + +/// Determine whether to use mergesort or quicksort. +fn flux_analyze( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) void { + const half1 = len / 2; + const quad1 = half1 / 2; + const quad2 = half1 - quad1; + const half2 = len - half1; + const quad3 = half2 / 2; + const quad4 = half2 - quad3; + + var ptr_a = array; + var ptr_b = array + quad1 * element_width; + var ptr_c = array + half1 * element_width; + var ptr_d = array + (half1 + quad3) * element_width; + + var streaks_a: u32 = 0; + var streaks_b: u32 = 0; + var streaks_c: u32 = 0; + var streaks_d: u32 = 0; + + var balance_a: usize = 0; + var balance_b: usize = 0; + var balance_c: usize = 0; + var balance_d: usize = 0; + + if (quad1 < quad2) { + // Must inc here, due to being in a branch. + const gt = compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) == GT; + balance_b += @intFromBool(gt); + ptr_b += element_width; + } + if (quad2 < quad3) { + // Must inc here, due to being in a branch. + const gt = compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) == GT; + balance_c += @intFromBool(gt); + ptr_c += element_width; + } + if (quad3 < quad3) { + // Must inc here, due to being in a branch. + balance_d += @intFromBool(compare_inc(cmp, cmp_data, ptr_d, ptr_d + element_width, data_is_owned, inc_n_data) == GT); + ptr_d += element_width; + } + + var sum_a: u8 = 0; + var sum_b: u8 = 0; + var sum_c: u8 = 0; + var sum_d: u8 = 0; + var count = len; + while (count > 132) : (count -= 128) { + // 32*4 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 32 * 4); + } + for (0..32) |_| { + sum_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); + ptr_a += element_width; + sum_b += @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); + ptr_b += element_width; + sum_c += @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); + ptr_c += element_width; + sum_d += @intFromBool(compare(cmp, cmp_data, ptr_d, ptr_d + element_width) == GT); + ptr_d += element_width; + } + balance_a += sum_a; + sum_a = @intFromBool((sum_a == 0) or (sum_a == 32)); + streaks_a += sum_a; + balance_b += sum_b; + sum_b = @intFromBool((sum_b == 0) or (sum_b == 32)); + streaks_b += sum_b; + balance_c += sum_c; + sum_c = @intFromBool((sum_c == 0) or (sum_c == 32)); + streaks_c += sum_c; + balance_d += sum_d; + sum_d = @intFromBool((sum_d == 0) or (sum_d == 32)); + streaks_d += sum_d; + + if (count > 516 and sum_a + sum_b + sum_c + sum_d == 0) { + balance_a += 48; + ptr_a += 96 * element_width; + balance_b += 48; + ptr_b += 96 * element_width; + balance_c += 48; + ptr_c += 96 * element_width; + balance_d += 48; + ptr_d += 96 * element_width; + count -= 384; + } + } + + // 4*divCeil(count-7, 4) guaranteed compares. + if (data_is_owned) { + const n: usize = std.math.divCeil(usize, count - 7, 4) catch unreachable; + inc_n_data(cmp_data, 4 * (n)); + } + while (count > 7) : (count -= 4) { + balance_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); + ptr_a += element_width; + balance_b += @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); + ptr_b += element_width; + balance_c += @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); + ptr_c += element_width; + balance_d += @intFromBool(compare(cmp, cmp_data, ptr_d, ptr_d + element_width) == GT); + ptr_d += element_width; + } + + count = balance_a + balance_b + balance_c + balance_d; + + if (count == 0) { + // The whole list may be ordered. Cool! + if (compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data) != GT and + compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) != GT and + compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) + return; + } + + // Not fully sorted, too bad. + sum_a = if (quad1 - balance_a == 1) 0 else 1; + sum_b = if (quad2 - balance_b == 1) 0 else 1; + sum_c = if (quad3 - balance_c == 1) 0 else 1; + sum_d = if (quad4 - balance_d == 1) 0 else 1; + + if (sum_a | sum_b | sum_c | sum_d != 0) { + // Any sum variable that is set is a reversed chunk of data. + const span1: u3 = @intFromBool((sum_a != 0 and sum_b != 0) and compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data) == GT); + const span2: u3 = @intFromBool((sum_b != 0 and sum_c != 0) and compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) == GT); + const span3: u3 = @intFromBool((sum_c != 0 and sum_d != 0) and compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) == GT); + + switch (span1 | (span2 << 1) | (span3 << 2)) { + 0 => {}, + 1 => { + quad_reversal(array, ptr_b, element_width, copy); + balance_a = 0; + balance_b = 0; + }, + 2 => { + quad_reversal(ptr_a + 1, ptr_c, element_width, copy); + balance_b = 0; + balance_c = 0; + }, + 3 => { + quad_reversal(array, ptr_c, element_width, copy); + balance_a = 0; + balance_b = 0; + balance_c = 0; + }, + 4 => { + quad_reversal(ptr_b + 1, ptr_d, element_width, copy); + balance_c = 0; + balance_d = 0; + }, + 5 => { + quad_reversal(array, ptr_b, element_width, copy); + balance_a = 0; + balance_b = 0; + quad_reversal(ptr_b + 1, ptr_d, element_width, copy); + balance_c = 0; + balance_d = 0; + }, + 6 => { + quad_reversal(ptr_a + 1, ptr_d, element_width, copy); + balance_b = 0; + balance_c = 0; + balance_d = 0; + }, + 7 => { + quad_reversal(array, ptr_d, element_width, copy); + return; + }, + } + // Indivial chunks that are reversed. + if (sum_a != 0 and balance_a != 0) { + quad_reversal(array, ptr_a, element_width, copy); + balance_a = 0; + } + if (sum_b != 0 and balance_b != 0) { + quad_reversal(ptr_a + element_width, ptr_b, element_width, copy); + balance_b = 0; + } + if (sum_c != 0 and balance_c != 0) { + quad_reversal(ptr_b + element_width, ptr_c, element_width, copy); + balance_c = 0; + } + if (sum_d != 0 and balance_d != 0) { + quad_reversal(ptr_c + element_width, ptr_d, element_width, copy); + balance_d = 0; + } + } + + // Switch to quadsort if at least 25% ordered. + count = len / 512; + + sum_a = @intFromBool(streaks_a > count); + sum_b = @intFromBool(streaks_b > count); + sum_c = @intFromBool(streaks_c > count); + sum_d = @intFromBool(streaks_d > count); + + // Always use quadsort if memory pressure is bad. + if (quad1 > QUAD_CACHE) { + sum_a = 1; + sum_b = 1; + sum_c = 1; + sum_d = 1; + } + switch (@as(u4, @intCast(sum_a | (sum_b << 1) | (sum_c << 2) | (sum_d << 3)))) { + 0 => { + flux_partition(array, swap, array, swap + len * element_width, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return; + }, + 1 => { + if (balance_a != 0) + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + (quad2 + half2) * element_width, quad2 + half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 2 => { + flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_b != 0) + quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + half2 * element_width, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 3 => { + if (balance_a != 0) + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_b != 0) + quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + half2 * element_width, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 4 => { + flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_c != 0) + quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad3 * element_width, quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 8 => { + flux_partition(array, swap, array, swap + (half1 + quad3) * element_width, (half1 + quad3), cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_d != 0) + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 9 => { + if (balance_a != 0) + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + (quad2 + quad3) * element_width, quad2 + quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_d != 0) + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 12 => { + flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_c != 0) + quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (balance_d != 0) + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + }, + 5, 6, 7, 10, 11, 13, 14, 15 => { + if (sum_a != 0) { + if (balance_a != 0) + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + if (sum_b != 0) { + if (balance_b != 0) + quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + quad2 * element_width, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + if (sum_c != 0) { + if (balance_c != 0) + quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + quad3 * element_width, quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + if (sum_d != 0) { + if (balance_d != 0) + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad4 * element_width, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + }, + } + // Final Merging of sorted partitions. + if (compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) != GT) { + // Lucky us, everything sorted. + return; + } + @memcpy(swap[0..(len * element_width)], array[0..(len * element_width)]); + } else { + // First half sorted, second half needs merge. + cross_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + @memcpy(swap[0..(half1 * element_width)], array[0..(half1 * element_width)]); + } + } else { + if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) { + // First half needs merge, second half sorted. + @memcpy((swap + half2 * element_width)[0..(half2 * element_width)], (array + half2 * element_width)[0..(half2 * element_width)]); + cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + // Both halves need merge. + cross_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + } + // Merge bach to original list. + cross_merge(swap, array, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); +} + +fn flux_partition( + array: [*]u8, + swap: [*]u8, + x: [*]u8, + pivot: [*]u8, + initial_len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) void { + var generic: i32 = 0; + + var pivot_ptr = pivot; + var x_ptr = x; + + var len = initial_len; + var arr_len: usize = 0; + var swap_len: usize = 0; + + while (true) { + pivot_ptr -= element_width; + + if (len <= 2048) { + median_of_nine(x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, pivot_ptr); + } else { + median_of_cbrt(array, swap, x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, &generic, pivot_ptr); + + if (generic != 0) { + if (x_ptr == swap) { + @memcpy(array[0..(len * element_width)], swap[0..(len * element_width)]); + } + quadsort_swap(array, len, swap, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return; + } + } + + if (arr_len != 0 and compare_inc(cmp, cmp_data, pivot_ptr + element_width, pivot_ptr, data_is_owned, inc_n_data) != GT) { + flux_reverse_partition(array, swap, array, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return; + } + arr_len = flux_default_partition(array, swap, array, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + swap_len = len - arr_len; + + if (arr_len <= swap_len / 32 or swap_len <= FLUX_OUT) { + if (arr_len == 0) + return; + if (swap_len == 0) { + flux_reverse_partition(array, swap, array, pivot_ptr, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return; + } + @memcpy((array + arr_len * element_width)[0..(swap_len * element_width)], swap[0..(swap_len * element_width)]); + quadsort_swap(array + arr_len * element_width, swap_len, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + flux_partition(array + arr_len * element_width, swap, swap, pivot_ptr, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + + if (swap_len <= arr_len / 32 or arr_len <= FLUX_OUT) { + if (arr_len <= FLUX_OUT) { + quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else { + flux_reverse_partition(array, swap, array, pivot_ptr, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + return; + } + len = arr_len; + x_ptr = array; + } +} + +// Improve generic data handling by mimickind dual pivot quicksort. + +fn flux_default_partition( + array: [*]u8, + swap: [*]u8, + x: [*]u8, + pivot: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) usize { + var arr_ptr = array; + var swap_ptr = swap; + var pivot_ptr = pivot; + var x_ptr = x; + + // len guaranteed compares + if (data_is_owned) { + inc_n_data(cmp_data, len); + } + var run: usize = 0; + var a: usize = 8; + while (a <= len) : (a += 8) { + inline for (0..8) |_| { + const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) != GT) &arr_ptr else &swap_ptr; + copy(from.*, x_ptr); + from.* += element_width; + x_ptr += element_width; + } + + if (arr_ptr == array or swap_ptr == swap) + run = a; + } + for (0..(len % 8)) |_| { + const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) != GT) &arr_ptr else &swap_ptr; + copy(from.*, x_ptr); + from.* += element_width; + x_ptr += element_width; + } + + const m = (@intFromPtr(arr_ptr) - @intFromPtr(array)) / element_width; + + if (run <= len / 4) { + return m; + } + + if (m == len) { + return m; + } + + a = len - m; + @memcpy((array + m * element_width)[0..(a * element_width)], swap[0..(a * element_width)]); + + quadsort_swap(array + m * element_width, a, swap, a, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, m, swap, m, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + + return 0; +} + +fn flux_reverse_partition( + array: [*]u8, + swap: [*]u8, + x: [*]u8, + pivot: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) void { + var arr_ptr = array; + var swap_ptr = swap; + var pivot_ptr = pivot; + var x_ptr = x; + + // len guaranteed compares + if (data_is_owned) { + inc_n_data(cmp_data, len); + } + for (0..(len / 8)) |_| { + inline for (0..8) |_| { + const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) == GT) &arr_ptr else &swap_ptr; + copy(from.*, x_ptr); + from.* += element_width; + x_ptr += element_width; + } + } + for (0..(len % 8)) |_| { + const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) == GT) &arr_ptr else &swap_ptr; + copy(from.*, x_ptr); + from.* += element_width; + x_ptr += element_width; + } + + const arr_len = (@intFromPtr(arr_ptr) - @intFromPtr(array)); + const swap_len = (@intFromPtr(swap_ptr) - @intFromPtr(swap)); + + @memcpy((array + arr_len)[0..swap_len], swap[0..swap_len]); + + if (swap_len <= arr_len / 16 or arr_len <= FLUX_OUT * element_width) { + quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } + flux_partition(array, swap, array, pivot, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); +} + +// ================ Pivot Selection =========================================== +// Used for selecting the quicksort pivot for various sized arrays. + +fn median_of_cbrt( + array: [*]u8, + swap: [*]u8, + x_ptr: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, + generic: *i32, + out: [*]u8, +) void { + var cbrt: usize = 32; + while (len > cbrt * cbrt * cbrt) : (cbrt *= 2) {} + + const div = len / cbrt; + + // I assume using the pointer as an int is to add randomness here? + var arr_ptr = x_ptr + @intFromPtr(&div) / 16 % div; + var swap_ptr = if (x_ptr == array) swap else array; + + for (0..cbrt) |cnt| { + copy(swap_ptr + cnt * element_width, arr_ptr); + arr_ptr += div; + } + cbrt /= 2; + + quadsort_swap(swap_ptr, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(swap_ptr + cbrt * element_width, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + + // 2 guaranteed compares + if (data_is_owned) { + inc_n_data(cmp_data, 2); + } + generic.* = @intFromBool(compare(cmp, cmp_data, swap_ptr + (cbrt * 2 - 1) * element_width, swap_ptr) != GT and compare(cmp, cmp_data, swap_ptr + (cbrt - 1) * element_width, swap_ptr) != GT); + + binary_median(swap_ptr, swap_ptr + cbrt * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, out); +} + +fn median_of_nine( + array: [*]u8, + len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, + out: [*]u8, +) void { + var buffer: [9 * MAX_ELEMENT_BUFFER_SIZE]u8 align(BufferAlign) = undefined; + const swap_ptr = @as([*]u8, @ptrCast(&buffer[0])); + + var arr_ptr = array; + + const offset = (len / 9) * element_width; + for (0..9) |x| { + copy(swap_ptr + x * element_width, arr_ptr); + arr_ptr += offset; + } + + trim_four(swap_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + trim_four(swap_ptr + 4 * element_width, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + + copy(swap_ptr, swap_ptr + 5 * element_width); + copy(swap_ptr + 3 * element_width, swap_ptr + 8 * element_width); + + trim_four(swap_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + + copy(swap_ptr, swap_ptr + 6 * element_width); + + // 3 guaranteed compares + if (data_is_owned) { + inc_n_data(cmp_data, 3); + } + const x = compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 1 * element_width) == GT; + const y = compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 2 * element_width) == GT; + const z = compare(cmp, cmp_data, swap_ptr + 1 * element_width, swap_ptr + 2 * element_width) == GT; + + const index: usize = @intFromBool(x == y) + (@intFromBool(y) ^ @intFromBool(z)); + copy(out, swap_ptr + index * element_width); +} + +fn trim_four( + initial_ptr_a: [*]u8, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) void { + var buffer: BufferType align(BufferAlign) = undefined; + const tmp_ptr = @as([*]u8, @ptrCast(&buffer[0])); + + // 4 guaranteed compares. + if (data_is_owned) { + inc_n_data(cmp_data, 4); + } + var ptr_a = initial_ptr_a; + { + const gt = compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT; + const x = if (gt) element_width else 0; + const not_x = if (!gt) element_width else 0; + copy(tmp_ptr, ptr_a + not_x); + copy(ptr_a, ptr_a + x); + copy(ptr_a + element_width, tmp_ptr); + ptr_a += 2 * element_width; + } + { + const gt = compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT; + const x = if (gt) element_width else 0; + const not_x = if (!gt) element_width else 0; + copy(tmp_ptr, ptr_a + not_x); + copy(ptr_a, ptr_a + x); + copy(ptr_a + element_width, tmp_ptr); + ptr_a -= 2 * element_width; + } + { + const lte = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width) != GT; + const x = if (lte) element_width else 0; + copy(ptr_a + 2 * element_width, ptr_a + x); + ptr_a += 1; + } + { + const gt = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width) == GT; + const x = if (gt) element_width else 0; + copy(ptr_a, ptr_a + x); + } +} + +fn binary_median( + initial_ptr_a: [*]u8, + initial_ptr_b: [*]u8, + initial_len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, + out: [*]u8, +) void { + var len = initial_len; + if (data_is_owned) { + // We need to increment log2 of n times. + // We can get that by counting leading zeros and of (top - 1). + // Needs to be `-1` so values that are powers of 2 don't sort up a bin. + // Then just add 1 back to the final result. + const log2 = @bitSizeOf(usize) - @clz(len - 1) + 1; + inc_n_data(cmp_data, log2); + } + var ptr_a = initial_ptr_a; + var ptr_b = initial_ptr_b; + while (len / 2 != 0) : (len /= 2) { + if (compare(cmp, cmp_data, ptr_a, ptr_b) != GT) { + ptr_a += len * element_width; + } else { + ptr_b += len * element_width; + } + } + var from = if (compare(cmp, cmp_data, ptr_a, ptr_b) == GT) ptr_a else ptr_b; + copy(out, from); +} + +// ================ Quadsort ================================================== +// The high level quadsort functions. + +/// A version of quadsort given pre-allocated swap memory. +/// This is a primitive needed fro fluxsort. +/// Will not allocate. +pub fn quadsort_swap( + array: [*]u8, + len: usize, + swap: [*]u8, + swap_len: usize, + cmp: CompareFn, + cmp_data: Opaque, + element_width: usize, + copy: CopyFn, + comptime data_is_owned: bool, + inc_n_data: IncN, +) void { + if (len < 96) { + tail_swap(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } else if (quad_swap(array, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data) != .sorted) { + const block_len = quad_merge(array, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + + rotate_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + } +} + pub fn quadsort( - source_ptr: [*]u8, + array: [*]u8, len: usize, cmp: CompareFn, cmp_data: Opaque, @@ -48,9 +843,9 @@ pub fn quadsort( // Also, for numeric types, inlining the compare function can be a 2x perf gain. if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { if (data_is_owned_runtime) { - quadsort_direct(source_ptr, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); + quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); } else { - quadsort_direct(source_ptr, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data); + quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data); } } else { if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { @@ -58,7 +853,7 @@ pub fn quadsort( var arr_ptr = @as([*]Opaque, @ptrCast(@alignCast(alloc_ptr))); defer utils.dealloc(alloc_ptr, @alignOf(usize)); for (0..len) |i| { - arr_ptr[i] = source_ptr + i * element_width; + arr_ptr[i] = array + i * element_width; } // Setup for indirect comparison. @@ -80,7 +875,7 @@ pub fn quadsort( } // Copy to original array as sorted. - @memcpy(source_ptr[0..(len * element_width)], collect_ptr[0..(len * element_width)]); + @memcpy(array[0..(len * element_width)], collect_ptr[0..(len * element_width)]); } else { roc_panic("Out of memory while trying to allocate for sorting", 0); } @@ -2780,7 +3575,7 @@ threadlocal var inner_cmp: ?CompareFn = null; pub fn indirect_compare(compare_data: Opaque, lhs_ptr: Opaque, rhs_ptr: Opaque) callconv(.C) u8 { const lhs = @as(*[*]u8, @ptrCast(@alignCast(lhs_ptr))).*; const rhs = @as(*[*]u8, @ptrCast(@alignCast(rhs_ptr))).*; - return (inner_cmp orelse unreachable)(compare_data, lhs, rhs); + return (inner_cmp.?)(compare_data, lhs, rhs); } pub fn pointer_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { From 6d7d9e4e57b97bee7f50350eb1e2b87a1fbf169e Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 00:03:46 -0700 Subject: [PATCH 110/203] add testing for pivot selection --- crates/compiler/builtins/bitcode/src/sort.zig | 179 +++++++++++++++--- 1 file changed, 157 insertions(+), 22 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index e09ac5bd532..097f8355974 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -457,7 +457,7 @@ fn flux_partition( comptime data_is_owned: bool, inc_n_data: IncN, ) void { - var generic: i32 = 0; + var generic = false; var pivot_ptr = pivot; var x_ptr = x; @@ -472,9 +472,9 @@ fn flux_partition( if (len <= 2048) { median_of_nine(x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, pivot_ptr); } else { - median_of_cbrt(array, swap, x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, &generic, pivot_ptr); + median_of_cube_root(array, swap, x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, &generic, pivot_ptr); - if (generic != 0) { + if (generic) { if (x_ptr == swap) { @memcpy(array[0..(len * element_width)], swap[0..(len * element_width)]); } @@ -630,7 +630,11 @@ fn flux_reverse_partition( // ================ Pivot Selection =========================================== // Used for selecting the quicksort pivot for various sized arrays. -fn median_of_cbrt( +/// Returns the median of an array taking roughly cube root samples. +/// Only used for super large arrays, assumes the minimum cube root is 32. +/// Out is set to the median. +/// Generic is set to true if all elements are the same. +fn median_of_cube_root( array: [*]u8, swap: [*]u8, x_ptr: [*]u8, @@ -641,7 +645,7 @@ fn median_of_cbrt( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, - generic: *i32, + generic: *bool, out: [*]u8, ) void { var cbrt: usize = 32; @@ -649,28 +653,25 @@ fn median_of_cbrt( const div = len / cbrt; - // I assume using the pointer as an int is to add randomness here? + // Using a pointer to div as an int is to get a radom offset from 0 to div. var arr_ptr = x_ptr + @intFromPtr(&div) / 16 % div; var swap_ptr = if (x_ptr == array) swap else array; for (0..cbrt) |cnt| { copy(swap_ptr + cnt * element_width, arr_ptr); - arr_ptr += div; + arr_ptr += div * element_width; } cbrt /= 2; quadsort_swap(swap_ptr, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); quadsort_swap(swap_ptr + cbrt * element_width, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - // 2 guaranteed compares - if (data_is_owned) { - inc_n_data(cmp_data, 2); - } - generic.* = @intFromBool(compare(cmp, cmp_data, swap_ptr + (cbrt * 2 - 1) * element_width, swap_ptr) != GT and compare(cmp, cmp_data, swap_ptr + (cbrt - 1) * element_width, swap_ptr) != GT); + generic.* = compare_inc(cmp, cmp_data, swap_ptr + (cbrt * 2 - 1) * element_width, swap_ptr, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, swap_ptr + (cbrt - 1) * element_width, swap_ptr, data_is_owned, inc_n_data) != GT; binary_median(swap_ptr, swap_ptr + cbrt * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, out); } +/// Returns the median of 9 evenly distributed elements from a list. fn median_of_nine( array: [*]u8, len: usize, @@ -711,10 +712,12 @@ fn median_of_nine( const y = compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 2 * element_width) == GT; const z = compare(cmp, cmp_data, swap_ptr + 1 * element_width, swap_ptr + 2 * element_width) == GT; - const index: usize = @intFromBool(x == y) + (@intFromBool(y) ^ @intFromBool(z)); + const index: usize = @as(usize, @intFromBool(x == y)) + (@intFromBool(y) ^ @intFromBool(z)); copy(out, swap_ptr + index * element_width); } +/// Ensures the middle two elements of the array are the middle two elements by sorting. +/// Does not care about the rest of the elements and can overwrite them. fn trim_four( initial_ptr_a: [*]u8, cmp: CompareFn, @@ -752,17 +755,19 @@ fn trim_four( } { const lte = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width) != GT; - const x = if (lte) element_width else 0; + const x = if (lte) 2 * element_width else 0; copy(ptr_a + 2 * element_width, ptr_a + x); - ptr_a += 1; + ptr_a += element_width; } { const gt = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width) == GT; - const x = if (gt) element_width else 0; + const x = if (gt) 2 * element_width else 0; copy(ptr_a, ptr_a + x); } } +/// Attempts to find the median of 2 binary arrays of len. +/// Set out to the larger median from the two lists. fn binary_median( initial_ptr_a: [*]u8, initial_ptr_b: [*]u8, @@ -777,16 +782,14 @@ fn binary_median( ) void { var len = initial_len; if (data_is_owned) { - // We need to increment log2 of n times. - // We can get that by counting leading zeros and of (top - 1). - // Needs to be `-1` so values that are powers of 2 don't sort up a bin. - // Then just add 1 back to the final result. - const log2 = @bitSizeOf(usize) - @clz(len - 1) + 1; + // We need to increment log2 of len times. + const log2 = @bitSizeOf(usize) - @clz(len); inc_n_data(cmp_data, log2); } var ptr_a = initial_ptr_a; var ptr_b = initial_ptr_b; - while (len / 2 != 0) : (len /= 2) { + len /= 2; + while (len != 0) : (len /= 2) { if (compare(cmp, cmp_data, ptr_a, ptr_b) != GT) { ptr_a += len * element_width; } else { @@ -797,6 +800,138 @@ fn binary_median( copy(out, from); } +test "median_of_cube_root" { + var test_count: i64 = 0; + var out: i64 = 0; + var generic = false; + + var swap: [32]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + { + var arr: [32]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [32]i64{ + // + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, + // + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, + }; + median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 17); + try testing.expectEqual(generic, false); + + for (0..32) |i| { + arr[i] = 7; + } + median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 7); + try testing.expectEqual(generic, true); + + for (0..32) |i| { + arr[i] = 7 + @as(i64, @intCast(i % 2)); + } + median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 8); + try testing.expectEqual(generic, false); + } +} + +test "median_of_nine" { + var test_count: i64 = 0; + var out: i64 = 0; + + { + var arr: [9]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + // Note: median is not guaranteed to be extact. in this case: + // [2, 3], [6, 7] -> [3, 6] -> [3, 6, 9] -> 6 + try testing.expectEqual(out, 6); + + arr = [9]i64{ 1, 3, 5, 7, 9, 2, 4, 6, 8 }; + median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + // Note: median is not guaranteed to be extact. in this case: + // [3, 5], [4, 6] -> [4, 5] -> [4, 5, 8] -> 5 + try testing.expectEqual(out, 5); + + arr = [9]i64{ 2, 3, 9, 4, 5, 7, 8, 6, 1 }; + median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + // Note: median is not guaranteed to be extact. in this case: + // [3, 4], [5, 6] -> [4, 5] -> [1, 4, 5] -> 4 + try testing.expectEqual(out, 4); + } +} + +test "trim_four" { + var test_count: i64 = 0; + + var arr: [4]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [4]i64{ 1, 2, 3, 4 }; + trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); + + arr = [4]i64{ 2, 3, 1, 4 }; + trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, [4]i64{ 2, 3, 2, 4 }); + + arr = [4]i64{ 4, 3, 2, 1 }; + trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, [4]i64{ 3, 2, 3, 2 }); +} + +test "binary_median" { + var test_count: i64 = 0; + var out: i64 = 0; + + { + var arr: [10]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + binary_median(arr_ptr, arr_ptr + 5 * @sizeOf(i64), 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 6); + + arr = [10]i64{ 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 }; + binary_median(arr_ptr, arr_ptr + 5 * @sizeOf(i64), 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 5); + } + { + var arr: [16]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + + arr = [16]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 9); + + arr = [16]i64{ 1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14, 16 }; + binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 9); + + arr = [16]i64{ 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8 }; + binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(out, 9); + } +} + // ================ Quadsort ================================================== // The high level quadsort functions. From f9eeee0bde54228227a16959c75773cbe43fac40 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 00:32:39 -0700 Subject: [PATCH 111/203] add test for flux_reverse_partition --- crates/compiler/builtins/bitcode/src/sort.zig | 92 ++++++++++++++++++- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 097f8355974..bbb59595202 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -518,6 +518,9 @@ fn flux_partition( // Improve generic data handling by mimickind dual pivot quicksort. +/// Partition x into array and swap. +/// Finally, copy from swap into array and finish sorting. +/// This is reverse cause it copies elements less than or equal to the pivot into the array. fn flux_default_partition( array: [*]u8, swap: [*]u8, @@ -579,6 +582,10 @@ fn flux_default_partition( return 0; } +/// Partition x into array and swap. +/// Finally, copy from swap into array and finish sorting the part of the array less than pivot. +/// This is reverse cause it copies elements greater than the pivot into the array. +/// Elements are expected to at most be the same size as the pivot. fn flux_reverse_partition( array: [*]u8, swap: [*]u8, @@ -616,17 +623,94 @@ fn flux_reverse_partition( x_ptr += element_width; } - const arr_len = (@intFromPtr(arr_ptr) - @intFromPtr(array)); - const swap_len = (@intFromPtr(swap_ptr) - @intFromPtr(swap)); + const arr_len = (@intFromPtr(arr_ptr) - @intFromPtr(array)) / element_width; + const swap_len = (@intFromPtr(swap_ptr) - @intFromPtr(swap)) / element_width; - @memcpy((array + arr_len)[0..swap_len], swap[0..swap_len]); + @memcpy((array + arr_len * element_width)[0..(swap_len * element_width)], swap[0..(swap_len * element_width)]); - if (swap_len <= arr_len / 16 or arr_len <= FLUX_OUT * element_width) { + if (swap_len <= arr_len / 16 or arr_len <= FLUX_OUT) { quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return; } flux_partition(array, swap, array, pivot, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } +test "flux_default_partition" { + const expected = [32]i64{ + // + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + // + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + }; + var test_count: i64 = 0; + var pivot: i64 = 0; + + var arr: [32]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [32]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [32]i64{ + // + 1, 3, 5, 7, 9, 11, 13, 15, 17, 17, 17, 17, 17, 17, 17, 17, + // + 2, 4, 6, 8, 10, 12, 14, 16, 17, 17, 17, 17, 17, 17, 17, 17, + }; + pivot = 16; + _ = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, expected); + + arr = [32]i64{ + // + 1, 17, 3, 17, 5, 17, 7, 17, 9, 17, 11, 17, 13, 17, 15, 17, + // + 17, 2, 17, 4, 17, 6, 17, 8, 17, 10, 17, 12, 17, 14, 17, 16, + }; + pivot = 16; + _ = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, expected); +} + +test "flux_reverse_partition" { + const expected = [32]i64{ + // + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + // + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + }; + var test_count: i64 = 0; + var pivot: i64 = 0; + + var arr: [32]i64 = undefined; + var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); + var swap: [32]i64 = undefined; + var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); + + arr = [32]i64{ + // + 1, 3, 5, 7, 9, 11, 13, 15, 17, 17, 17, 17, 17, 17, 17, 17, + // + 2, 4, 6, 8, 10, 12, 14, 16, 17, 17, 17, 17, 17, 17, 17, 17, + }; + pivot = 17; + flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, expected); + + arr = [32]i64{ + // + 1, 17, 3, 17, 5, 17, 7, 17, 9, 17, 11, 17, 13, 17, 15, 17, + // + 17, 2, 17, 4, 17, 6, 17, 8, 17, 10, 17, 12, 17, 14, 17, 16, + }; + pivot = 17; + flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, expected); +} + // ================ Pivot Selection =========================================== // Used for selecting the quicksort pivot for various sized arrays. From eff37f6b6dca8ea0337b134545567ffa5b12310a Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 11:21:21 -0700 Subject: [PATCH 112/203] fix up flux_default_parition and add tests --- crates/compiler/builtins/bitcode/src/sort.zig | 127 ++++++++++++------ 1 file changed, 86 insertions(+), 41 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index bbb59595202..f1872b06526 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -518,9 +518,14 @@ fn flux_partition( // Improve generic data handling by mimickind dual pivot quicksort. -/// Partition x into array and swap. -/// Finally, copy from swap into array and finish sorting. -/// This is reverse cause it copies elements less than or equal to the pivot into the array. +/// Partition x into array and swap (less than or equal to pivot). +/// Finally, copy from swap into array and potentially finish sorting. +/// Will return early if the array is highly unordered (allows for more quicksort). +/// Will return early if all elements went before the pivot (maybe all elements are same and can reverse parition next?). +/// Othewise, will complete the sort with quadsort. +/// +/// Warning, on early return, the paritions of the array will be split over array and swap. +/// The returned size is the number of elements in the array. fn flux_default_partition( array: [*]u8, swap: [*]u8, @@ -547,7 +552,7 @@ fn flux_default_partition( var a: usize = 8; while (a <= len) : (a += 8) { inline for (0..8) |_| { - const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) != GT) &arr_ptr else &swap_ptr; + const from = if (compare(cmp, cmp_data, x_ptr, pivot_ptr) != GT) &arr_ptr else &swap_ptr; copy(from.*, x_ptr); from.* += element_width; x_ptr += element_width; @@ -557,7 +562,7 @@ fn flux_default_partition( run = a; } for (0..(len % 8)) |_| { - const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) != GT) &arr_ptr else &swap_ptr; + const from = if (compare(cmp, cmp_data, x_ptr, pivot_ptr) != GT) &arr_ptr else &swap_ptr; copy(from.*, x_ptr); from.* += element_width; x_ptr += element_width; @@ -565,14 +570,17 @@ fn flux_default_partition( const m = (@intFromPtr(arr_ptr) - @intFromPtr(array)) / element_width; + // Not very sorted, early return and allow for more quicksort. if (run <= len / 4) { return m; } + // Bad pivot? All elements went before it. if (m == len) { return m; } + // Significantly sorted, finish with quadsort. a = len - m; @memcpy((array + m * element_width)[0..(a * element_width)], swap[0..(a * element_width)]); @@ -636,12 +644,7 @@ fn flux_reverse_partition( } test "flux_default_partition" { - const expected = [32]i64{ - // - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - // - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - }; + var expected: [32]i64 = undefined; var test_count: i64 = 0; var pivot: i64 = 0; @@ -651,33 +654,82 @@ test "flux_default_partition" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [32]i64{ - // - 1, 3, 5, 7, 9, 11, 13, 15, 17, 17, 17, 17, 17, 17, 17, 17, - // - 2, 4, 6, 8, 10, 12, 14, 16, 17, 17, 17, 17, 17, 17, 17, 17, + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, + }; + expected = [32]i64{ + // <= pivot first half + 1, 3, 5, 7, 9, 11, 13, 15, + // <= pivot second half + 2, 4, 6, 8, 10, 12, 14, 16, + // > pivot first half + 17, 19, 21, 23, 25, 27, 29, 31, + // > pivot second half + 18, 20, 22, 24, 26, 28, 30, 32, }; pivot = 16; - _ = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + var arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); try testing.expectEqual(test_count, 0); - try testing.expectEqual(arr, expected); + try testing.expectEqual(arr_len, 16); + try testing.expectEqualSlices(i64, arr[0..16], expected[0..16]); + try testing.expectEqualSlices(i64, swap[0..16], expected[16..32]); arr = [32]i64{ - // - 1, 17, 3, 17, 5, 17, 7, 17, 9, 17, 11, 17, 13, 17, 15, 17, - // - 17, 2, 17, 4, 17, 6, 17, 8, 17, 10, 17, 12, 17, 14, 17, 16, + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, + }; + expected = [32]i64{ + // <= pivot first half + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, + // <= pivot second half + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, + // > pivot first half + 25, 27, 29, 31, + // > pivot second half + 26, 28, 30, 32, + }; + pivot = 24; + arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr_len, 24); + try testing.expectEqualSlices(i64, arr[0..24], expected[0..24]); + try testing.expectEqualSlices(i64, swap[0..8], expected[24..32]); + + arr = [32]i64{ + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, }; + expected = [32]i64{ + // <= pivot first half + 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, + // <= pivot second half + 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, + }; + pivot = 32; + arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr_len, 32); + try testing.expectEqualSlices(i64, arr[0..32], expected[0..32]); + + arr = [32]i64{ + 1, 3, 5, 7, 9, 11, 13, 15, + 2, 4, 6, 8, 10, 12, 14, 16, + 18, 20, 22, 24, 26, 28, 30, 32, + 17, 19, 21, 23, 25, 27, 29, 31, + }; + for (0..31) |i| { + expected[i] = @intCast(i + 1); + } pivot = 16; - _ = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); try testing.expectEqual(test_count, 0); - try testing.expectEqual(arr, expected); + try testing.expectEqual(arr_len, 0); + try testing.expectEqualSlices(i64, arr[0..32], expected[0..32]); } test "flux_reverse_partition" { const expected = [32]i64{ - // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - // 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, }; var test_count: i64 = 0; @@ -689,9 +741,7 @@ test "flux_reverse_partition" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [32]i64{ - // 1, 3, 5, 7, 9, 11, 13, 15, 17, 17, 17, 17, 17, 17, 17, 17, - // 2, 4, 6, 8, 10, 12, 14, 16, 17, 17, 17, 17, 17, 17, 17, 17, }; pivot = 17; @@ -700,15 +750,22 @@ test "flux_reverse_partition" { try testing.expectEqual(arr, expected); arr = [32]i64{ - // 1, 17, 3, 17, 5, 17, 7, 17, 9, 17, 11, 17, 13, 17, 15, 17, - // 17, 2, 17, 4, 17, 6, 17, 8, 17, 10, 17, 12, 17, 14, 17, 16, }; pivot = 17; flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); + + arr = [32]i64{ + 15, 17, 13, 17, 11, 17, 9, 17, 7, 17, 5, 17, 3, 17, 1, 17, + 17, 16, 17, 14, 17, 12, 17, 10, 17, 8, 17, 6, 17, 4, 17, 2, + }; + pivot = 17; + flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + try testing.expectEqual(test_count, 0); + try testing.expectEqual(arr, expected); } // ================ Pivot Selection =========================================== @@ -896,9 +953,7 @@ test "median_of_cube_root" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [32]i64{ - // 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, - // 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, }; median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); @@ -2862,13 +2917,11 @@ test "quad_swap" { arr = [75]i64{ // multiple ordered chunks 1, 3, 5, 7, 9, 11, 13, 15, - // 33, 34, 35, 36, 37, 38, 39, 40, // partially ordered 41, 42, 45, 46, 43, 44, 47, 48, // multiple reverse chunks 70, 69, 68, 67, 66, 65, 64, 63, - // 16, 14, 12, 10, 8, 6, 4, 2, // another ordered 49, 50, 51, 52, 53, 54, 55, 56, @@ -2878,7 +2931,6 @@ test "quad_swap" { 32, 31, 28, 27, 30, 29, 26, 25, // awkward tail 62, 59, 61, 60, 71, 73, 75, 74, - // 72, 58, 57, }; @@ -2888,23 +2940,16 @@ test "quad_swap" { try testing.expectEqual(arr, [75]i64{ // first 32 elements sorted (with 8 reversed that get flipped here) 1, 2, 3, 4, 5, 6, 7, 8, - // 9, 10, 11, 12, 13, 14, 15, 16, - // 33, 34, 35, 36, 37, 38, 39, 40, - // 41, 42, 43, 44, 45, 46, 47, 48, // second 32 elements sorted (with 8 reversed that get flipped here) 17, 18, 19, 20, 21, 22, 23, 24, - // 25, 26, 27, 28, 29, 30, 31, 32, - // 49, 50, 51, 52, 53, 54, 55, 56, - // 63, 64, 65, 66, 67, 68, 69, 70, // awkward tail 57, 58, 59, 60, 61, 62, 71, 72, - // 73, 74, 75, }); From 65d7f6ad567e78f538bf03fb24e78b31ec843c3c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 11:29:48 -0700 Subject: [PATCH 113/203] update fuzzing to fluxsort --- crates/compiler/builtins/bitcode/src/fuzz_sort.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index 14b5e6d05d9..7d4e49eb244 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -37,7 +37,7 @@ pub fn fuzz_main() !void { } var test_count: i64 = 0; - sort.quadsort(@ptrCast(arr_ptr), size, &test_i64_compare_refcounted, @ptrCast(&test_count), true, &test_inc_n_data, @sizeOf(i64), @alignOf(i64), &test_i64_copy); + sort.fluxsort(@ptrCast(arr_ptr), size, &test_i64_compare_refcounted, @ptrCast(&test_count), true, &test_inc_n_data, @sizeOf(i64), @alignOf(i64), &test_i64_copy); const sorted = std.sort.isSorted(i64, arr_ptr[0..size], {}, std.sort.asc(i64)); if (DEBUG) { From 51a3ea655c5cfc893e3faf57e9d37e755bfedf33 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 11:58:36 -0700 Subject: [PATCH 114/203] minor cleanup --- crates/compiler/builtins/bitcode/src/sort.zig | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index f1872b06526..87cb56a21dd 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -191,6 +191,10 @@ fn flux_analyze( if (data_is_owned) { inc_n_data(cmp_data, 32 * 4); } + sum_a = 0; + sum_b = 0; + sum_c = 0; + sum_d = 0; for (0..32) |_| { sum_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); ptr_a += element_width; @@ -254,16 +258,20 @@ fn flux_analyze( } // Not fully sorted, too bad. - sum_a = if (quad1 - balance_a == 1) 0 else 1; - sum_b = if (quad2 - balance_b == 1) 0 else 1; - sum_c = if (quad3 - balance_c == 1) 0 else 1; - sum_d = if (quad4 - balance_d == 1) 0 else 1; + sum_a = @intFromBool(quad1 - balance_a == 1); + sum_b = @intFromBool(quad2 - balance_b == 1); + sum_c = @intFromBool(quad3 - balance_c == 1); + sum_d = @intFromBool(quad4 - balance_d == 1); if (sum_a | sum_b | sum_c | sum_d != 0) { + // 3 compares guaranteed. + if (data_is_owned) { + inc_n_data(cmp_data, 3); + } // Any sum variable that is set is a reversed chunk of data. - const span1: u3 = @intFromBool((sum_a != 0 and sum_b != 0) and compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data) == GT); - const span2: u3 = @intFromBool((sum_b != 0 and sum_c != 0) and compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) == GT); - const span3: u3 = @intFromBool((sum_c != 0 and sum_d != 0) and compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) == GT); + const span1: u3 = @intFromBool(sum_a != 0 and sum_b != 0) * @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); + const span2: u3 = @intFromBool(sum_b != 0 and sum_c != 0) * @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); + const span3: u3 = @intFromBool(sum_c != 0 and sum_d != 0) * @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); switch (span1 | (span2 << 1) | (span3 << 2)) { 0 => {}, From 109dba836ebf75d03e2e5aedf300c6f8b4d089e3 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 16:04:44 -0700 Subject: [PATCH 115/203] clarify comments and variable names, fix small bug --- crates/compiler/builtins/bitcode/src/sort.zig | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 87cb56a21dd..8731615b069 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -181,20 +181,16 @@ fn flux_analyze( ptr_d += element_width; } - var sum_a: u8 = 0; - var sum_b: u8 = 0; - var sum_c: u8 = 0; - var sum_d: u8 = 0; var count = len; while (count > 132) : (count -= 128) { // 32*4 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 32 * 4); } - sum_a = 0; - sum_b = 0; - sum_c = 0; - sum_d = 0; + var sum_a: u8 = 0; + var sum_b: u8 = 0; + var sum_c: u8 = 0; + var sum_d: u8 = 0; for (0..32) |_| { sum_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); ptr_a += element_width; @@ -258,20 +254,20 @@ fn flux_analyze( } // Not fully sorted, too bad. - sum_a = @intFromBool(quad1 - balance_a == 1); - sum_b = @intFromBool(quad2 - balance_b == 1); - sum_c = @intFromBool(quad3 - balance_c == 1); - sum_d = @intFromBool(quad4 - balance_d == 1); + const reversed_a = quad1 - balance_a == 1; + const reversed_b = quad2 - balance_b == 1; + const reversed_c = quad3 - balance_c == 1; + const reversed_d = quad4 - balance_d == 1; - if (sum_a | sum_b | sum_c | sum_d != 0) { + const reversed_any = reversed_a or reversed_b or reversed_c or reversed_d; + if (reversed_any) { // 3 compares guaranteed. if (data_is_owned) { inc_n_data(cmp_data, 3); } - // Any sum variable that is set is a reversed chunk of data. - const span1: u3 = @intFromBool(sum_a != 0 and sum_b != 0) * @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); - const span2: u3 = @intFromBool(sum_b != 0 and sum_c != 0) * @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); - const span3: u3 = @intFromBool(sum_c != 0 and sum_d != 0) * @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); + const span1: u3 = @intFromBool(reversed_a and reversed_b) * @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); + const span2: u3 = @intFromBool(reversed_b and reversed_c) * @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); + const span3: u3 = @intFromBool(reversed_c and reversed_d) * @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); switch (span1 | (span2 << 1) | (span3 << 2)) { 0 => {}, @@ -316,19 +312,19 @@ fn flux_analyze( }, } // Indivial chunks that are reversed. - if (sum_a != 0 and balance_a != 0) { + if (reversed_a and balance_a != 0) { quad_reversal(array, ptr_a, element_width, copy); balance_a = 0; } - if (sum_b != 0 and balance_b != 0) { + if (reversed_b and balance_b != 0) { quad_reversal(ptr_a + element_width, ptr_b, element_width, copy); balance_b = 0; } - if (sum_c != 0 and balance_c != 0) { + if (reversed_c and balance_c != 0) { quad_reversal(ptr_b + element_width, ptr_c, element_width, copy); balance_c = 0; } - if (sum_d != 0 and balance_d != 0) { + if (reversed_d and balance_d != 0) { quad_reversal(ptr_c + element_width, ptr_d, element_width, copy); balance_d = 0; } @@ -337,19 +333,19 @@ fn flux_analyze( // Switch to quadsort if at least 25% ordered. count = len / 512; - sum_a = @intFromBool(streaks_a > count); - sum_b = @intFromBool(streaks_b > count); - sum_c = @intFromBool(streaks_c > count); - sum_d = @intFromBool(streaks_d > count); + var ordered_a: u4 = @intFromBool(streaks_a > count); + var ordered_b: u4 = @intFromBool(streaks_b > count); + var ordered_c: u4 = @intFromBool(streaks_c > count); + var ordered_d: u4 = @intFromBool(streaks_d > count); // Always use quadsort if memory pressure is bad. if (quad1 > QUAD_CACHE) { - sum_a = 1; - sum_b = 1; - sum_c = 1; - sum_d = 1; + ordered_a = 1; + ordered_b = 1; + ordered_c = 1; + ordered_d = 1; } - switch (@as(u4, @intCast(sum_a | (sum_b << 1) | (sum_c << 2) | (sum_d << 3)))) { + switch (ordered_a | (ordered_b << 1) | (ordered_c << 2) | (ordered_d << 3)) { 0 => { flux_partition(array, swap, array, swap + len * element_width, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return; @@ -398,25 +394,25 @@ fn flux_analyze( quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 5, 6, 7, 10, 11, 13, 14, 15 => { - if (sum_a != 0) { + if (ordered_a != 0) { if (balance_a != 0) quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } - if (sum_b != 0) { + if (ordered_b != 0) { if (balance_b != 0) quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + quad2 * element_width, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } - if (sum_c != 0) { + if (ordered_c != 0) { if (balance_c != 0) quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + quad3 * element_width, quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } - if (sum_d != 0) { + if (ordered_d != 0) { if (balance_d != 0) quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { @@ -483,6 +479,7 @@ fn flux_partition( median_of_cube_root(array, swap, x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, &generic, pivot_ptr); if (generic) { + // Tons of identical elements, quadsort. if (x_ptr == swap) { @memcpy(array[0..(len * element_width)], swap[0..(len * element_width)]); } @@ -492,12 +489,16 @@ fn flux_partition( } if (arr_len != 0 and compare_inc(cmp, cmp_data, pivot_ptr + element_width, pivot_ptr, data_is_owned, inc_n_data) != GT) { + // pivot equals the last pivot, reverse partition and everything is done. flux_reverse_partition(array, swap, array, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return; } - arr_len = flux_default_partition(array, swap, array, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + // arr_len is elements <= pivot. + // swap_len is elements > pivot. + arr_len = flux_default_partition(array, swap, x_ptr, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); swap_len = len - arr_len; + // If highly imbalanced try a different strategy. if (arr_len <= swap_len / 32 or swap_len <= FLUX_OUT) { if (arr_len == 0) return; @@ -511,6 +512,7 @@ fn flux_partition( flux_partition(array + arr_len * element_width, swap, swap, pivot_ptr, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } + // If highly imbalanced try a different strategy if (swap_len <= arr_len / 32 or arr_len <= FLUX_OUT) { if (arr_len <= FLUX_OUT) { quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); @@ -782,7 +784,7 @@ test "flux_reverse_partition" { /// Returns the median of an array taking roughly cube root samples. /// Only used for super large arrays, assumes the minimum cube root is 32. /// Out is set to the median. -/// Generic is set to true if all elements are the same. +/// Generic is set to true if all elements selected for the median are the same. fn median_of_cube_root( array: [*]u8, swap: [*]u8, @@ -802,8 +804,8 @@ fn median_of_cube_root( const div = len / cbrt; - // Using a pointer to div as an int is to get a radom offset from 0 to div. - var arr_ptr = x_ptr + @intFromPtr(&div) / 16 % div; + // Using a pointer to div as an int is to get a random offset from 0 to div. + var arr_ptr = x_ptr + (@intFromPtr(&div) / 16 % div) * element_width; var swap_ptr = if (x_ptr == array) swap else array; for (0..cbrt) |cnt| { @@ -857,11 +859,11 @@ fn median_of_nine( if (data_is_owned) { inc_n_data(cmp_data, 3); } - const x = compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 1 * element_width) == GT; - const y = compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 2 * element_width) == GT; - const z = compare(cmp, cmp_data, swap_ptr + 1 * element_width, swap_ptr + 2 * element_width) == GT; + const x: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 1 * element_width) == GT); + const y: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 2 * element_width) == GT); + const z: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 1 * element_width, swap_ptr + 2 * element_width) == GT); - const index: usize = @as(usize, @intFromBool(x == y)) + (@intFromBool(y) ^ @intFromBool(z)); + const index = @intFromBool(x == y) + (x ^ z); copy(out, swap_ptr + index * element_width); } From 285c43e288ded051ee3c2ccadc5aaa5a67f97ef5 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 16:26:31 -0700 Subject: [PATCH 116/203] make fuzzer better and add missing else --- crates/compiler/builtins/bitcode/src/fuzz_sort.zig | 11 ++++++----- crates/compiler/builtins/bitcode/src/sort.zig | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig index 7d4e49eb244..b1581aa2e6f 100644 --- a/crates/compiler/builtins/bitcode/src/fuzz_sort.zig +++ b/crates/compiler/builtins/bitcode/src/fuzz_sort.zig @@ -29,19 +29,19 @@ pub fn fuzz_main() !void { const data = try stdin.readToEndAlloc(allocator, std.math.maxInt(usize)); defer allocator.free(data); - const size = data.len / @sizeOf(i64); + const len = data.len / @sizeOf(i64); const arr_ptr: [*]i64 = @alignCast(@ptrCast(data.ptr)); if (DEBUG) { - std.debug.print("Input: [{d}]{d}\n", .{ size, arr_ptr[0..size] }); + std.debug.print("Input: [{d}]{d}\n", .{ len, arr_ptr[0..len] }); } var test_count: i64 = 0; - sort.fluxsort(@ptrCast(arr_ptr), size, &test_i64_compare_refcounted, @ptrCast(&test_count), true, &test_inc_n_data, @sizeOf(i64), @alignOf(i64), &test_i64_copy); + sort.fluxsort(@ptrCast(arr_ptr), len, &test_i64_compare_refcounted, @ptrCast(&test_count), true, &test_inc_n_data, @sizeOf(i64), @alignOf(i64), &test_i64_copy); - const sorted = std.sort.isSorted(i64, arr_ptr[0..size], {}, std.sort.asc(i64)); + const sorted = std.sort.isSorted(i64, arr_ptr[0..len], {}, std.sort.asc(i64)); if (DEBUG) { - std.debug.print("Output: [{d}]{d}\nSorted: {}\nFinal RC: {}\n", .{ size, arr_ptr[0..size], sorted, test_count }); + std.debug.print("Output: [{d}]{d}\nSorted: {}\nFinal RC: {}\n", .{ len, arr_ptr[0..len], sorted, test_count }); } std.debug.assert(sorted); std.debug.assert(test_count == 0); @@ -55,6 +55,7 @@ fn test_i64_compare_refcounted(count_ptr: Opaque, a_ptr: Opaque, b_ptr: Opaque) const gt = @as(u8, @intFromBool(a > b)); const lt = @as(u8, @intFromBool(a < b)); + std.debug.assert(@as(*isize, @ptrCast(@alignCast(count_ptr))).* > 0); @as(*isize, @ptrCast(@alignCast(count_ptr))).* -= 1; // Eq = 0 // GT = 1 diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 8731615b069..52d309aeddf 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -52,8 +52,7 @@ pub fn fluxsort( if (len < 132) { // Just quadsort it. quadsort(array, len, cmp, cmp_data, data_is_owned_runtime, inc_n_data, element_width, alignment, copy); - } - if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { + } else if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { if (data_is_owned_runtime) { fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); } else { From 400393a531d2d7313d603f645a29c5bdb4813977 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 16:34:28 -0700 Subject: [PATCH 117/203] remove wrong assert --- crates/compiler/builtins/bitcode/src/sort.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 52d309aeddf..3d1c64376b8 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -3017,7 +3017,7 @@ test "quad_reversal" { // Below are functions for sorting under 32 element arrays. /// Uses swap space to sort the tail of an array. -/// The array should be under 32 elements in length. +/// The array should generally be under 32 elements in length. fn tail_swap( array: [*]u8, len: usize, @@ -3029,8 +3029,6 @@ fn tail_swap( comptime data_is_owned: bool, inc_n_data: IncN, ) void { - std.debug.assert(len < 32); - if (len < 8) { tiny_sort(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); return; From 7747f06504b2c01edfa38ca69a4ae4a9e7447f13 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 17:31:19 -0700 Subject: [PATCH 118/203] fix a few bugs caught by fuzzing --- crates/compiler/builtins/bitcode/src/sort.zig | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 3d1c64376b8..4a4a2e9bafb 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -168,13 +168,13 @@ fn flux_analyze( balance_b += @intFromBool(gt); ptr_b += element_width; } - if (quad2 < quad3) { + if (quad1 < quad3) { // Must inc here, due to being in a branch. const gt = compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) == GT; balance_c += @intFromBool(gt); ptr_c += element_width; } - if (quad3 < quad3) { + if (quad1 < quad4) { // Must inc here, due to being in a branch. balance_d += @intFromBool(compare_inc(cmp, cmp_data, ptr_d, ptr_d + element_width, data_is_owned, inc_n_data) == GT); ptr_d += element_width; @@ -226,10 +226,12 @@ fn flux_analyze( } } - // 4*divCeil(count-7, 4) guaranteed compares. if (data_is_owned) { - const n: usize = std.math.divCeil(usize, count - 7, 4) catch unreachable; - inc_n_data(cmp_data, 4 * (n)); + if (count > 7) { + // 4*divCeil(count-7, 4) guaranteed compares. + const n: usize = std.math.divCeil(usize, count - 7, 4) catch unreachable; + inc_n_data(cmp_data, 4 * n); + } } while (count > 7) : (count -= 4) { balance_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); @@ -371,7 +373,7 @@ fn flux_analyze( flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); if (balance_c != 0) quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad3 * element_width, quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad4 * element_width, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); }, 8 => { flux_partition(array, swap, array, swap + (half1 + quad3) * element_width, (half1 + quad3), cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); @@ -439,12 +441,12 @@ fn flux_analyze( cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { // Both halves need merge. - cross_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap + half1 * element_width, ptr_b + element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } } // Merge bach to original list. - cross_merge(swap, array, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } fn flux_partition( From 22896d309ac6220cd95ef410540a9cac71e0dd45 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 17:52:59 -0700 Subject: [PATCH 119/203] fix a another bug caught by fuzzing --- crates/compiler/builtins/bitcode/src/sort.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 4a4a2e9bafb..dd4bd0b0ad2 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -437,7 +437,7 @@ fn flux_analyze( } else { if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) { // First half needs merge, second half sorted. - @memcpy((swap + half2 * element_width)[0..(half2 * element_width)], (array + half2 * element_width)[0..(half2 * element_width)]); + @memcpy((swap + half1 * element_width)[0..(half2 * element_width)], (array + half1 * element_width)[0..(half2 * element_width)]); cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); } else { // Both halves need merge. From 90d3bb6a08078816e36b2db6c287fd923f270fb8 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 18:52:41 -0700 Subject: [PATCH 120/203] wire indirect through everything as a comptime bool --- crates/compiler/builtins/bitcode/src/sort.zig | 891 +++++++++--------- 1 file changed, 465 insertions(+), 426 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index dd4bd0b0ad2..60ad645d3de 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -54,9 +54,9 @@ pub fn fluxsort( quadsort(array, len, cmp, cmp_data, data_is_owned_runtime, inc_n_data, element_width, alignment, copy); } else if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { if (data_is_owned_runtime) { - fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); + fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data, false); } else { - fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data); + fluxsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data, false); } } else { if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { @@ -67,15 +67,11 @@ pub fn fluxsort( arr_ptr[i] = array + i * element_width; } - // Setup for indirect comparison. - inner_cmp = cmp; - defer inner_cmp = null; - // Sort. if (data_is_owned_runtime) { - fluxsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, true, inc_n_data); + fluxsort_direct(@ptrCast(arr_ptr), len, cmp, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, true, inc_n_data, true); } else { - fluxsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, false, inc_n_data); + fluxsort_direct(@ptrCast(arr_ptr), len, cmp, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, false, inc_n_data, true); } if (utils.alloc(len * element_width, alignment)) |collect_ptr| { @@ -106,14 +102,15 @@ fn fluxsort_direct( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { if (utils.alloc(len * element_width, alignment)) |swap| { - flux_analyze(array, len, swap, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_analyze(array, len, swap, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); utils.dealloc(swap, alignment); } else { // Fallback to quadsort. It has ways to use less memory. - quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, data_is_owned, inc_n_data); + quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, data_is_owned, inc_n_data, indirect); } } @@ -139,6 +136,7 @@ fn flux_analyze( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { const half1 = len / 2; const quad1 = half1 / 2; @@ -164,19 +162,19 @@ fn flux_analyze( if (quad1 < quad2) { // Must inc here, due to being in a branch. - const gt = compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) == GT; + const gt = compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data, indirect) == GT; balance_b += @intFromBool(gt); ptr_b += element_width; } if (quad1 < quad3) { // Must inc here, due to being in a branch. - const gt = compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) == GT; + const gt = compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data, indirect) == GT; balance_c += @intFromBool(gt); ptr_c += element_width; } if (quad1 < quad4) { // Must inc here, due to being in a branch. - balance_d += @intFromBool(compare_inc(cmp, cmp_data, ptr_d, ptr_d + element_width, data_is_owned, inc_n_data) == GT); + balance_d += @intFromBool(compare_inc(cmp, cmp_data, ptr_d, ptr_d + element_width, data_is_owned, inc_n_data, indirect) == GT); ptr_d += element_width; } @@ -191,13 +189,13 @@ fn flux_analyze( var sum_c: u8 = 0; var sum_d: u8 = 0; for (0..32) |_| { - sum_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); + sum_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width, indirect) == GT); ptr_a += element_width; - sum_b += @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); + sum_b += @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width, indirect) == GT); ptr_b += element_width; - sum_c += @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); + sum_c += @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width, indirect) == GT); ptr_c += element_width; - sum_d += @intFromBool(compare(cmp, cmp_data, ptr_d, ptr_d + element_width) == GT); + sum_d += @intFromBool(compare(cmp, cmp_data, ptr_d, ptr_d + element_width, indirect) == GT); ptr_d += element_width; } balance_a += sum_a; @@ -234,13 +232,13 @@ fn flux_analyze( } } while (count > 7) : (count -= 4) { - balance_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); + balance_a += @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width, indirect) == GT); ptr_a += element_width; - balance_b += @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); + balance_b += @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width, indirect) == GT); ptr_b += element_width; - balance_c += @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); + balance_c += @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width, indirect) == GT); ptr_c += element_width; - balance_d += @intFromBool(compare(cmp, cmp_data, ptr_d, ptr_d + element_width) == GT); + balance_d += @intFromBool(compare(cmp, cmp_data, ptr_d, ptr_d + element_width, indirect) == GT); ptr_d += element_width; } @@ -248,9 +246,9 @@ fn flux_analyze( if (count == 0) { // The whole list may be ordered. Cool! - if (compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data) != GT and - compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) != GT and - compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) + if (compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data, indirect) != GT and + compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data, indirect) != GT and + compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data, indirect) != GT) return; } @@ -266,9 +264,9 @@ fn flux_analyze( if (data_is_owned) { inc_n_data(cmp_data, 3); } - const span1: u3 = @intFromBool(reversed_a and reversed_b) * @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT); - const span2: u3 = @intFromBool(reversed_b and reversed_c) * @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width) == GT); - const span3: u3 = @intFromBool(reversed_c and reversed_d) * @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width) == GT); + const span1: u3 = @intFromBool(reversed_a and reversed_b) * @intFromBool(compare(cmp, cmp_data, ptr_a, ptr_a + element_width, indirect) == GT); + const span2: u3 = @intFromBool(reversed_b and reversed_c) * @intFromBool(compare(cmp, cmp_data, ptr_b, ptr_b + element_width, indirect) == GT); + const span3: u3 = @intFromBool(reversed_c and reversed_d) * @intFromBool(compare(cmp, cmp_data, ptr_c, ptr_c + element_width, indirect) == GT); switch (span1 | (span2 << 1) | (span3 << 2)) { 0 => {}, @@ -348,105 +346,105 @@ fn flux_analyze( } switch (ordered_a | (ordered_b << 1) | (ordered_c << 2) | (ordered_d << 3)) { 0 => { - flux_partition(array, swap, array, swap + len * element_width, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, swap + len * element_width, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; }, 1 => { if (balance_a != 0) - quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + (quad2 + half2) * element_width, quad2 + half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + (quad2 + half2) * element_width, quad2 + half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 2 => { - flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_b != 0) - quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + half2 * element_width, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + half2 * element_width, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 3 => { if (balance_a != 0) - quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_b != 0) - quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + half2 * element_width, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + half2 * element_width, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 4 => { - flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_c != 0) - quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad4 * element_width, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad4 * element_width, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 8 => { - flux_partition(array, swap, array, swap + (half1 + quad3) * element_width, (half1 + quad3), cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, swap + (half1 + quad3) * element_width, (half1 + quad3), cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_d != 0) - quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 9 => { if (balance_a != 0) - quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + (quad2 + quad3) * element_width, quad2 + quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + (quad2 + quad3) * element_width, quad2 + quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_d != 0) - quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 12 => { - flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, swap + half1 * element_width, half1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_c != 0) - quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (balance_d != 0) - quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 5, 6, 7, 10, 11, 13, 14, 15 => { if (ordered_a != 0) { if (balance_a != 0) - quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, quad1, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, swap + quad1 * element_width, quad1, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } if (ordered_b != 0) { if (balance_b != 0) - quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_a + element_width, quad2, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + quad2 * element_width, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_a + element_width, swap, ptr_a + element_width, swap + quad2 * element_width, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } if (ordered_c != 0) { if (balance_c != 0) - quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_b + element_width, quad3, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + quad3 * element_width, quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_b + element_width, swap, ptr_b + element_width, swap + quad3 * element_width, quad3, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } if (ordered_d != 0) { if (balance_d != 0) - quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(ptr_c + element_width, quad4, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad4 * element_width, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(ptr_c + element_width, swap, ptr_c + element_width, swap + quad4 * element_width, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } }, } // Final Merging of sorted partitions. - if (compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data) != GT) { - if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) { - if (compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, ptr_a, ptr_a + element_width, data_is_owned, inc_n_data, indirect) != GT) { + if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data, indirect) != GT) { + if (compare_inc(cmp, cmp_data, ptr_b, ptr_b + element_width, data_is_owned, inc_n_data, indirect) != GT) { // Lucky us, everything sorted. return; } @memcpy(swap[0..(len * element_width)], array[0..(len * element_width)]); } else { // First half sorted, second half needs merge. - cross_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); @memcpy(swap[0..(half1 * element_width)], array[0..(half1 * element_width)]); } } else { - if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, ptr_c, ptr_c + element_width, data_is_owned, inc_n_data, indirect) != GT) { // First half needs merge, second half sorted. @memcpy((swap + half1 * element_width)[0..(half2 * element_width)], (array + half1 * element_width)[0..(half2 * element_width)]); - cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { // Both halves need merge. - cross_merge(swap + half1 * element_width, ptr_b + element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap + half1 * element_width, ptr_b + element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + cross_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } } // Merge bach to original list. - cross_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } fn flux_partition( @@ -461,6 +459,7 @@ fn flux_partition( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var generic = false; @@ -475,28 +474,28 @@ fn flux_partition( pivot_ptr -= element_width; if (len <= 2048) { - median_of_nine(x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, pivot_ptr); + median_of_nine(x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, pivot_ptr, indirect); } else { - median_of_cube_root(array, swap, x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, &generic, pivot_ptr); + median_of_cube_root(array, swap, x_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, &generic, pivot_ptr, indirect); if (generic) { // Tons of identical elements, quadsort. if (x_ptr == swap) { @memcpy(array[0..(len * element_width)], swap[0..(len * element_width)]); } - quadsort_swap(array, len, swap, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, len, swap, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } } - if (arr_len != 0 and compare_inc(cmp, cmp_data, pivot_ptr + element_width, pivot_ptr, data_is_owned, inc_n_data) != GT) { + if (arr_len != 0 and compare_inc(cmp, cmp_data, pivot_ptr + element_width, pivot_ptr, data_is_owned, inc_n_data, indirect) != GT) { // pivot equals the last pivot, reverse partition and everything is done. - flux_reverse_partition(array, swap, array, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_reverse_partition(array, swap, array, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } // arr_len is elements <= pivot. // swap_len is elements > pivot. - arr_len = flux_default_partition(array, swap, x_ptr, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + arr_len = flux_default_partition(array, swap, x_ptr, pivot_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); swap_len = len - arr_len; // If highly imbalanced try a different strategy. @@ -504,21 +503,21 @@ fn flux_partition( if (arr_len == 0) return; if (swap_len == 0) { - flux_reverse_partition(array, swap, array, pivot_ptr, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_reverse_partition(array, swap, array, pivot_ptr, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } @memcpy((array + arr_len * element_width)[0..(swap_len * element_width)], swap[0..(swap_len * element_width)]); - quadsort_swap(array + arr_len * element_width, swap_len, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array + arr_len * element_width, swap_len, swap, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - flux_partition(array + arr_len * element_width, swap, swap, pivot_ptr, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array + arr_len * element_width, swap, swap, pivot_ptr, swap_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } // If highly imbalanced try a different strategy if (swap_len <= arr_len / 32 or arr_len <= FLUX_OUT) { if (arr_len <= FLUX_OUT) { - quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - flux_reverse_partition(array, swap, array, pivot_ptr, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_reverse_partition(array, swap, array, pivot_ptr, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return; } @@ -549,6 +548,7 @@ fn flux_default_partition( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) usize { var arr_ptr = array; var swap_ptr = swap; @@ -563,7 +563,7 @@ fn flux_default_partition( var a: usize = 8; while (a <= len) : (a += 8) { inline for (0..8) |_| { - const from = if (compare(cmp, cmp_data, x_ptr, pivot_ptr) != GT) &arr_ptr else &swap_ptr; + const from = if (compare(cmp, cmp_data, x_ptr, pivot_ptr, indirect) != GT) &arr_ptr else &swap_ptr; copy(from.*, x_ptr); from.* += element_width; x_ptr += element_width; @@ -573,7 +573,7 @@ fn flux_default_partition( run = a; } for (0..(len % 8)) |_| { - const from = if (compare(cmp, cmp_data, x_ptr, pivot_ptr) != GT) &arr_ptr else &swap_ptr; + const from = if (compare(cmp, cmp_data, x_ptr, pivot_ptr, indirect) != GT) &arr_ptr else &swap_ptr; copy(from.*, x_ptr); from.* += element_width; x_ptr += element_width; @@ -595,8 +595,8 @@ fn flux_default_partition( a = len - m; @memcpy((array + m * element_width)[0..(a * element_width)], swap[0..(a * element_width)]); - quadsort_swap(array + m * element_width, a, swap, a, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - quadsort_swap(array, m, swap, m, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array + m * element_width, a, swap, a, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + quadsort_swap(array, m, swap, m, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return 0; } @@ -617,6 +617,7 @@ fn flux_reverse_partition( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var arr_ptr = array; var swap_ptr = swap; @@ -629,14 +630,14 @@ fn flux_reverse_partition( } for (0..(len / 8)) |_| { inline for (0..8) |_| { - const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) == GT) &arr_ptr else &swap_ptr; + const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr, indirect) == GT) &arr_ptr else &swap_ptr; copy(from.*, x_ptr); from.* += element_width; x_ptr += element_width; } } for (0..(len % 8)) |_| { - const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr) == GT) &arr_ptr else &swap_ptr; + const from = if (compare(cmp, cmp_data, pivot_ptr, x_ptr, indirect) == GT) &arr_ptr else &swap_ptr; copy(from.*, x_ptr); from.* += element_width; x_ptr += element_width; @@ -648,10 +649,10 @@ fn flux_reverse_partition( @memcpy((array + arr_len * element_width)[0..(swap_len * element_width)], swap[0..(swap_len * element_width)]); if (swap_len <= arr_len / 16 or arr_len <= FLUX_OUT) { - quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(array, arr_len, swap, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } - flux_partition(array, swap, array, pivot, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + flux_partition(array, swap, array, pivot, arr_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } test "flux_default_partition" { @@ -679,7 +680,7 @@ test "flux_default_partition" { 18, 20, 22, 24, 26, 28, 30, 32, }; pivot = 16; - var arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + var arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr_len, 16); try testing.expectEqualSlices(i64, arr[0..16], expected[0..16]); @@ -700,7 +701,7 @@ test "flux_default_partition" { 26, 28, 30, 32, }; pivot = 24; - arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr_len, 24); try testing.expectEqualSlices(i64, arr[0..24], expected[0..24]); @@ -717,7 +718,7 @@ test "flux_default_partition" { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, }; pivot = 32; - arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr_len, 32); try testing.expectEqualSlices(i64, arr[0..32], expected[0..32]); @@ -732,7 +733,7 @@ test "flux_default_partition" { expected[i] = @intCast(i + 1); } pivot = 16; - arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + arr_len = flux_default_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr_len, 0); try testing.expectEqualSlices(i64, arr[0..32], expected[0..32]); @@ -756,7 +757,7 @@ test "flux_reverse_partition" { 2, 4, 6, 8, 10, 12, 14, 16, 17, 17, 17, 17, 17, 17, 17, 17, }; pivot = 17; - flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); @@ -765,7 +766,7 @@ test "flux_reverse_partition" { 17, 2, 17, 4, 17, 6, 17, 8, 17, 10, 17, 12, 17, 14, 17, 16, }; pivot = 17; - flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); @@ -774,7 +775,7 @@ test "flux_reverse_partition" { 17, 16, 17, 14, 17, 12, 17, 10, 17, 8, 17, 6, 17, 4, 17, 2, }; pivot = 17; - flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + flux_reverse_partition(arr_ptr, swap_ptr, arr_ptr, @ptrCast(&pivot), 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -799,6 +800,7 @@ fn median_of_cube_root( inc_n_data: IncN, generic: *bool, out: [*]u8, + comptime indirect: bool, ) void { var cbrt: usize = 32; while (len > cbrt * cbrt * cbrt) : (cbrt *= 2) {} @@ -815,12 +817,12 @@ fn median_of_cube_root( } cbrt /= 2; - quadsort_swap(swap_ptr, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - quadsort_swap(swap_ptr + cbrt * element_width, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quadsort_swap(swap_ptr, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + quadsort_swap(swap_ptr + cbrt * element_width, cbrt, swap_ptr + cbrt * 2 * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); - generic.* = compare_inc(cmp, cmp_data, swap_ptr + (cbrt * 2 - 1) * element_width, swap_ptr, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, swap_ptr + (cbrt - 1) * element_width, swap_ptr, data_is_owned, inc_n_data) != GT; + generic.* = compare_inc(cmp, cmp_data, swap_ptr + (cbrt * 2 - 1) * element_width, swap_ptr, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, swap_ptr + (cbrt - 1) * element_width, swap_ptr, data_is_owned, inc_n_data, indirect) != GT; - binary_median(swap_ptr, swap_ptr + cbrt * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, out); + binary_median(swap_ptr, swap_ptr + cbrt * element_width, cbrt, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, out, indirect); } /// Returns the median of 9 evenly distributed elements from a list. @@ -834,6 +836,7 @@ fn median_of_nine( comptime data_is_owned: bool, inc_n_data: IncN, out: [*]u8, + comptime indirect: bool, ) void { var buffer: [9 * MAX_ELEMENT_BUFFER_SIZE]u8 align(BufferAlign) = undefined; const swap_ptr = @as([*]u8, @ptrCast(&buffer[0])); @@ -846,13 +849,13 @@ fn median_of_nine( arr_ptr += offset; } - trim_four(swap_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - trim_four(swap_ptr + 4 * element_width, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + trim_four(swap_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + trim_four(swap_ptr + 4 * element_width, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); copy(swap_ptr, swap_ptr + 5 * element_width); copy(swap_ptr + 3 * element_width, swap_ptr + 8 * element_width); - trim_four(swap_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + trim_four(swap_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); copy(swap_ptr, swap_ptr + 6 * element_width); @@ -860,9 +863,9 @@ fn median_of_nine( if (data_is_owned) { inc_n_data(cmp_data, 3); } - const x: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 1 * element_width) == GT); - const y: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 2 * element_width) == GT); - const z: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 1 * element_width, swap_ptr + 2 * element_width) == GT); + const x: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 1 * element_width, indirect) == GT); + const y: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 0 * element_width, swap_ptr + 2 * element_width, indirect) == GT); + const z: usize = @intFromBool(compare(cmp, cmp_data, swap_ptr + 1 * element_width, swap_ptr + 2 * element_width, indirect) == GT); const index = @intFromBool(x == y) + (x ^ z); copy(out, swap_ptr + index * element_width); @@ -878,6 +881,7 @@ fn trim_four( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var buffer: BufferType align(BufferAlign) = undefined; const tmp_ptr = @as([*]u8, @ptrCast(&buffer[0])); @@ -888,7 +892,7 @@ fn trim_four( } var ptr_a = initial_ptr_a; { - const gt = compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT; + const gt = compare(cmp, cmp_data, ptr_a, ptr_a + element_width, indirect) == GT; const x = if (gt) element_width else 0; const not_x = if (!gt) element_width else 0; copy(tmp_ptr, ptr_a + not_x); @@ -897,7 +901,7 @@ fn trim_four( ptr_a += 2 * element_width; } { - const gt = compare(cmp, cmp_data, ptr_a, ptr_a + element_width) == GT; + const gt = compare(cmp, cmp_data, ptr_a, ptr_a + element_width, indirect) == GT; const x = if (gt) element_width else 0; const not_x = if (!gt) element_width else 0; copy(tmp_ptr, ptr_a + not_x); @@ -906,13 +910,13 @@ fn trim_four( ptr_a -= 2 * element_width; } { - const lte = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width) != GT; + const lte = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width, indirect) != GT; const x = if (lte) 2 * element_width else 0; copy(ptr_a + 2 * element_width, ptr_a + x); ptr_a += element_width; } { - const gt = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width) == GT; + const gt = compare(cmp, cmp_data, ptr_a, ptr_a + 2 * element_width, indirect) == GT; const x = if (gt) 2 * element_width else 0; copy(ptr_a, ptr_a + x); } @@ -931,6 +935,7 @@ fn binary_median( comptime data_is_owned: bool, inc_n_data: IncN, out: [*]u8, + comptime indirect: bool, ) void { var len = initial_len; if (data_is_owned) { @@ -942,13 +947,13 @@ fn binary_median( var ptr_b = initial_ptr_b; len /= 2; while (len != 0) : (len /= 2) { - if (compare(cmp, cmp_data, ptr_a, ptr_b) != GT) { + if (compare(cmp, cmp_data, ptr_a, ptr_b, indirect) != GT) { ptr_a += len * element_width; } else { ptr_b += len * element_width; } } - var from = if (compare(cmp, cmp_data, ptr_a, ptr_b) == GT) ptr_a else ptr_b; + var from = if (compare(cmp, cmp_data, ptr_a, ptr_b, indirect) == GT) ptr_a else ptr_b; copy(out, from); } @@ -967,7 +972,7 @@ test "median_of_cube_root" { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, }; - median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); + median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 17); try testing.expectEqual(generic, false); @@ -975,7 +980,7 @@ test "median_of_cube_root" { for (0..32) |i| { arr[i] = 7; } - median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); + median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 7); try testing.expectEqual(generic, true); @@ -983,7 +988,7 @@ test "median_of_cube_root" { for (0..32) |i| { arr[i] = 7 + @as(i64, @intCast(i % 2)); } - median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out)); + median_of_cube_root(arr_ptr, swap_ptr, arr_ptr, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&generic), @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 8); try testing.expectEqual(generic, false); @@ -999,21 +1004,21 @@ test "median_of_nine" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); // Note: median is not guaranteed to be extact. in this case: // [2, 3], [6, 7] -> [3, 6] -> [3, 6, 9] -> 6 try testing.expectEqual(out, 6); arr = [9]i64{ 1, 3, 5, 7, 9, 2, 4, 6, 8 }; - median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); // Note: median is not guaranteed to be extact. in this case: // [3, 5], [4, 6] -> [4, 5] -> [4, 5, 8] -> 5 try testing.expectEqual(out, 5); arr = [9]i64{ 2, 3, 9, 4, 5, 7, 8, 6, 1 }; - median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + median_of_nine(arr_ptr, 10, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); // Note: median is not guaranteed to be extact. in this case: // [3, 4], [5, 6] -> [4, 5] -> [1, 4, 5] -> 4 @@ -1028,17 +1033,17 @@ test "trim_four" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [4]i64{ 1, 2, 3, 4 }; - trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 3, 1, 4 }; - trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 2, 3, 2, 4 }); arr = [4]i64{ 4, 3, 2, 1 }; - trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + trim_four(arr_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 3, 2, 3, 2 }); } @@ -1052,12 +1057,12 @@ test "binary_median" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - binary_median(arr_ptr, arr_ptr + 5 * @sizeOf(i64), 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + binary_median(arr_ptr, arr_ptr + 5 * @sizeOf(i64), 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 6); arr = [10]i64{ 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 }; - binary_median(arr_ptr, arr_ptr + 5 * @sizeOf(i64), 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + binary_median(arr_ptr, arr_ptr + 5 * @sizeOf(i64), 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 5); } @@ -1066,17 +1071,17 @@ test "binary_median" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [16]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; - binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 9); arr = [16]i64{ 1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14, 16 }; - binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 9); arr = [16]i64{ 9, 10, 11, 12, 13, 14, 15, 16, 1, 2, 3, 4, 5, 6, 7, 8 }; - binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out)); + binary_median(arr_ptr, arr_ptr + 8 * @sizeOf(i64), 8, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, @ptrCast(&out), false); try testing.expectEqual(test_count, 0); try testing.expectEqual(out, 9); } @@ -1099,13 +1104,14 @@ pub fn quadsort_swap( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { if (len < 96) { - tail_swap(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - } else if (quad_swap(array, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data) != .sorted) { - const block_len = quad_merge(array, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_swap(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + } else if (quad_swap(array, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect) != .sorted) { + const block_len = quad_merge(array, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); - rotate_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } } @@ -1128,9 +1134,9 @@ pub fn quadsort( // Also, for numeric types, inlining the compare function can be a 2x perf gain. if (element_width <= MAX_ELEMENT_BUFFER_SIZE) { if (data_is_owned_runtime) { - quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data); + quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, true, inc_n_data, false); } else { - quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data); + quadsort_direct(array, len, cmp, cmp_data, element_width, alignment, copy, false, inc_n_data, false); } } else { if (utils.alloc(len * @sizeOf(usize), @alignOf(usize))) |alloc_ptr| { @@ -1141,15 +1147,11 @@ pub fn quadsort( arr_ptr[i] = array + i * element_width; } - // Setup for indirect comparison. - inner_cmp = cmp; - defer inner_cmp = null; - // Sort. if (data_is_owned_runtime) { - quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, true, inc_n_data); + quadsort_direct(@ptrCast(arr_ptr), len, cmp, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, true, inc_n_data, true); } else { - quadsort_direct(@ptrCast(arr_ptr), len, indirect_compare, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, false, inc_n_data); + quadsort_direct(@ptrCast(arr_ptr), len, cmp, cmp_data, @sizeOf(usize), @alignOf(usize), &pointer_copy, false, inc_n_data, true); } if (utils.alloc(len * element_width, alignment)) |collect_ptr| { @@ -1180,6 +1182,7 @@ fn quadsort_direct( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var arr_ptr = array; if (len < 32) { @@ -1189,8 +1192,8 @@ fn quadsort_direct( // Also, zig doesn't hav alloca, so we always do max size here. var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 32]u8 align(BufferAlign) = undefined; const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); - tail_swap(arr_ptr, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data) != .sorted) { + tail_swap(arr_ptr, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + } else if (quad_swap(arr_ptr, len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect) != .sorted) { var swap_len = len; // This is optional, for about 5% perf hit, lower memory usage on large arrays. @@ -1200,14 +1203,14 @@ fn quadsort_direct( // } if (utils.alloc(swap_len * element_width, alignment)) |swap| { - const block_len = quad_merge(arr_ptr, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + const block_len = quad_merge(arr_ptr, len, swap, swap_len, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); - rotate_merge(arr_ptr, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge(arr_ptr, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); utils.dealloc(swap, alignment); } else { // Fallback to still sort even when out of memory. - @call(.never_inline, quadsort_stack_swap, .{ arr_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy }); + @call(.never_inline, quadsort_stack_swap, .{ arr_ptr, len, cmp, cmp_data, data_is_owned, inc_n_data, element_width, copy, indirect }); } } } @@ -1221,14 +1224,15 @@ fn quadsort_stack_swap( inc_n_data: IncN, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { // Use a 512 element on stack swap buffer. var swap_buffer: [MAX_ELEMENT_BUFFER_SIZE * 512]u8 align(BufferAlign) = undefined; const swap = @as([*]u8, @ptrCast(&swap_buffer[0])); - const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + const block_len = quad_merge(array, len, swap, 512, 32, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); - rotate_merge(array, len, swap, 512, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge(array, len, swap, 512, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } // ================ Inplace Rotate Merge ====================================== @@ -1247,11 +1251,12 @@ fn rotate_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var end_ptr = array + len * element_width; if (len <= block_len * 2 and len -% block_len <= swap_len) { - partial_backwards_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_backwards_merge(array, len, swap, swap_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } @@ -1260,11 +1265,11 @@ fn rotate_merge( var arr_ptr = array; while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += current_block_len * 2 * element_width) { if (@intFromPtr(arr_ptr) + current_block_len * 2 * element_width < @intFromPtr(end_ptr)) { - rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); continue; } const right_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width - current_block_len; - rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge_block(arr_ptr, swap, swap_len, current_block_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); break; } } @@ -1283,6 +1288,7 @@ fn rotate_merge_block( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var left_block = initial_left_block; var right = initial_right; @@ -1290,7 +1296,7 @@ fn rotate_merge_block( if (data_is_owned) { inc_n_data(cmp_data, 1); } - if (compare(cmp, cmp_data, array + (left_block - 1) * element_width, array + left_block * element_width) != GT) { + if (compare(cmp, cmp_data, array + (left_block - 1) * element_width, array + left_block * element_width, indirect) != GT) { // Lucky us, already sorted. return; } @@ -1298,7 +1304,7 @@ fn rotate_merge_block( var right_block = left_block / 2; left_block -= right_block; - var left = monobound_binary_first(array + (left_block + right_block) * element_width, right, array + left_block * element_width, cmp, cmp_data, element_width, data_is_owned, inc_n_data); + var left = monobound_binary_first(array + (left_block + right_block) * element_width, right, array + left_block * element_width, cmp, cmp_data, element_width, data_is_owned, inc_n_data, indirect); right -= left; if (left != 0) { @@ -1307,17 +1313,17 @@ fn rotate_merge_block( @memcpy((swap + left_block * element_width)[0..(left * element_width)], (array + (left_block + right_block) * element_width)[0..(left * element_width)]); std.mem.copyBackwards(u8, (array + (left + left_block) * element_width)[0..(right_block * element_width)], (array + left_block * element_width)[0..(right_block * element_width)]); - cross_merge(array, swap, left_block, left, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(array, swap, left_block, left, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { trinity_rotation(array + left_block * element_width, right_block + left, swap, swap_len, right_block, element_width, copy); const unbalanced = (left * 2 < left_block) or (left_block * 2 < left); if (unbalanced and left <= swap_len) { - partial_backwards_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_backwards_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else if (unbalanced and left_block <= swap_len) { - partial_forward_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_forward_merge(array, left_block + left, swap, swap_len, left_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - rotate_merge_block(array, swap, swap_len, left_block, left, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge_block(array, swap, swap_len, left_block, left, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } } } @@ -1325,11 +1331,11 @@ fn rotate_merge_block( if (right != 0) { const unbalanced = (right * 2 < right_block) or (right_block * 2 < right); if ((unbalanced and right <= swap_len) or right + right_block <= swap_len) { - partial_backwards_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_backwards_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else if (unbalanced and left_block <= swap_len) { - partial_forward_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_forward_merge(array + (left_block + left) * element_width, right_block + right, swap, swap_len, right_block, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } else { - rotate_merge_block(array + (left_block + left) * element_width, swap, swap_len, right_block, right, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + rotate_merge_block(array + (left_block + left) * element_width, swap, swap_len, right_block, right, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } } } @@ -1344,6 +1350,7 @@ fn monobound_binary_first( element_width: usize, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) usize { var top = initial_top; var end_ptr = array + top * element_width; @@ -1359,13 +1366,13 @@ fn monobound_binary_first( while (top > 1) { const mid = top / 2; - if (compare(cmp, cmp_data, value_ptr, end_ptr - mid * element_width) != GT) { + if (compare(cmp, cmp_data, value_ptr, end_ptr - mid * element_width, indirect) != GT) { end_ptr -= mid * element_width; } top -= mid; } - if (compare(cmp, cmp_data, value_ptr, end_ptr - element_width) != GT) { + if (compare(cmp, cmp_data, value_ptr, end_ptr - element_width, indirect) != GT) { end_ptr -= element_width; } return (@intFromPtr(end_ptr) - @intFromPtr(array)) / element_width; @@ -1541,23 +1548,23 @@ test "rotate_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - rotate_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + rotate_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - rotate_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + rotate_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - rotate_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + rotate_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // Limited swap, can't finish merge arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - rotate_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + rotate_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -1571,27 +1578,27 @@ test "monobound_binary_first" { var value_ptr = @as([*]u8, @ptrCast(&value)); value = 7; - var res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + var res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 3); value = 39; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 19); value = 40; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 20); value = -10; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 0); value = 10000; - res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data); + res = monobound_binary_first(arr_ptr, 25, value_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(res, 25); } @@ -1661,6 +1668,7 @@ fn tail_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { const end_ptr = array + len * element_width; var current_block_len = block_len; @@ -1668,11 +1676,11 @@ fn tail_merge( var arr_ptr = array; while (@intFromPtr(arr_ptr) + current_block_len * element_width < @intFromPtr(end_ptr)) : (arr_ptr += 2 * current_block_len * element_width) { if (@intFromPtr(arr_ptr) + 2 * current_block_len * element_width < @intFromPtr(end_ptr)) { - partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_backwards_merge(arr_ptr, 2 * current_block_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); continue; } const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; - partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + partial_backwards_merge(arr_ptr, rem_len, swap, swap_len, current_block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); break; } } @@ -1692,6 +1700,7 @@ fn partial_backwards_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { std.debug.assert(swap_len >= block_len); @@ -1707,7 +1716,7 @@ fn partial_backwards_merge( if (data_is_owned) { inc_n_data(cmp_data, 1); } - if (compare(cmp, cmp_data, left_tail, left_tail + element_width) != GT) { + if (compare(cmp, cmp_data, left_tail, left_tail + element_width, indirect) != GT) { // Lucky case, blocks happen to be sorted. return; } @@ -1716,7 +1725,7 @@ fn partial_backwards_merge( if (len <= swap_len and right_len >= 64) { // Large remaining merge and we have enough space to just do it in swap. - cross_merge(swap, array, block_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap, array, block_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); @memcpy(array[0..(element_width * len)], swap[0..(element_width * len)]); @@ -1730,7 +1739,7 @@ fn partial_backwards_merge( // For backards, we first try to do really large chunks, of 16 elements. outer: while (@intFromPtr(left_tail) > @intFromPtr(array + 16 * element_width) and @intFromPtr(right_tail) > @intFromPtr(swap + 16 * element_width)) { // Due to if looping, these must use `compare_inc` - while (compare_inc(cmp, cmp_data, left_tail, right_tail - 15 * element_width, data_is_owned, inc_n_data) != GT) { + while (compare_inc(cmp, cmp_data, left_tail, right_tail - 15 * element_width, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..16) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; @@ -1740,7 +1749,7 @@ fn partial_backwards_merge( break :outer; } // Due to if looping, these must use `compare_inc` - while (compare_inc(cmp, cmp_data, left_tail - 15 * element_width, right_tail, data_is_owned, inc_n_data) == GT) { + while (compare_inc(cmp, cmp_data, left_tail - 15 * element_width, right_tail, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..16) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -1753,13 +1762,13 @@ fn partial_backwards_merge( var loops: usize = 8; while (true) { // Due to if else chain and uncertain calling, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, left_tail, right_tail - element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, left_tail, right_tail - element_width, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..2) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; right_tail -= element_width; } - } else if (compare_inc(cmp, cmp_data, left_tail - element_width, right_tail, data_is_owned, inc_n_data) == GT) { + } else if (compare_inc(cmp, cmp_data, left_tail - element_width, right_tail, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..2) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -1771,7 +1780,7 @@ fn partial_backwards_merge( if (data_is_owned) { inc_n_data(cmp_data, 2); } - const lte = compare(cmp, cmp_data, left_tail, right_tail) != GT; + const lte = compare(cmp, cmp_data, left_tail, right_tail, indirect) != GT; var x = if (lte) element_width else 0; var not_x = if (!lte) element_width else 0; dest_tail -= element_width; @@ -1781,7 +1790,7 @@ fn partial_backwards_merge( left_tail -= element_width; dest_tail -= element_width; - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy, indirect); } loops -= 1; @@ -1797,7 +1806,7 @@ fn partial_backwards_merge( // The C use `goto` to implement the two tail recursive functions below inline. // I think the closest equivalent in zig would be to use an enum and a switch. // That would potentially optimize to computed gotos. - const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + const break_loop = partial_forward_merge_right_tail_2(&dest_tail, &array, &left_tail, &swap, &right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (break_loop) break; @@ -1806,7 +1815,7 @@ fn partial_backwards_merge( inc_n_data(cmp_data, 2); } // Couldn't move two elements, do a cross swap and continue. - const lte = compare(cmp, cmp_data, left_tail, right_tail) != GT; + const lte = compare(cmp, cmp_data, left_tail, right_tail, indirect) != GT; var x = if (lte) element_width else 0; var not_x = if (!lte) element_width else 0; dest_tail -= element_width; @@ -1816,7 +1825,7 @@ fn partial_backwards_merge( left_tail -= element_width; dest_tail -= element_width; - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy, indirect); } // Deal with tail. @@ -1826,7 +1835,7 @@ fn partial_backwards_merge( if (data_is_owned) { inc_n_data(cmp_data, 1); } - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy, indirect); } while (@intFromPtr(right_tail) >= @intFromPtr(swap)) { copy(dest_tail, right_tail); @@ -1850,26 +1859,27 @@ fn partial_forward_merge_right_tail_2( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) bool { - if (compare_inc(cmp, cmp_data, left_tail.*, right_tail.* - element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, left_tail.*, right_tail.* - element_width, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..2) |_| { copy(dest.*, right_tail.*); dest.* -= element_width; right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } - if (compare_inc(cmp, cmp_data, left_tail.* - element_width, right_tail.*, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, left_tail.* - element_width, right_tail.*, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..2) |_| { copy(dest.*, left_tail.*); dest.* -= element_width; left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } @@ -1888,26 +1898,27 @@ fn partial_forward_merge_left_tail_2( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) bool { - if (compare_inc(cmp, cmp_data, left_tail.* - element_width, right_tail.*, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, left_tail.* - element_width, right_tail.*, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..2) |_| { copy(dest.*, left_tail.*); dest.* -= element_width; left_tail.* -= element_width; } if (@intFromPtr(left_tail.*) > @intFromPtr(left_head.*) + element_width) { - return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_left_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } - if (compare_inc(cmp, cmp_data, left_tail.*, right_tail.* - element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, left_tail.*, right_tail.* - element_width, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..2) |_| { copy(dest.*, right_tail.*); dest.* -= element_width; right_tail.* -= element_width; } if (@intFromPtr(right_tail.*) > @intFromPtr(right_head.*) + element_width) { - return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_right_tail_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } @@ -1928,6 +1939,7 @@ fn partial_forward_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { std.debug.assert(swap_len >= block_len); @@ -1943,7 +1955,7 @@ fn partial_forward_merge( if (data_is_owned) { inc_n_data(cmp_data, 1); } - if (compare(cmp, cmp_data, right_head - element_width, right_head) != GT) { + if (compare(cmp, cmp_data, right_head - element_width, right_head, indirect) != GT) { // Lucky case, blocks happen to be sorted. return; } @@ -1961,7 +1973,7 @@ fn partial_forward_merge( // The C use `goto` to implement the two tail recursive functions below inline. // I think the closest equivalent in zig would be to use an enum and a switch. // That would potentially optimize to computed gotos. - const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + const break_loop = partial_forward_merge_right_head_2(&dest_head, &left_head, &left_tail, &right_head, &right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); if (break_loop) break; @@ -1970,7 +1982,7 @@ fn partial_forward_merge( inc_n_data(cmp_data, 2); } // Couldn't move two elements, do a cross swap and continue. - const lte = compare(cmp, cmp_data, left_head, right_head) != GT; + const lte = compare(cmp, cmp_data, left_head, right_head, indirect) != GT; var x = if (lte) element_width else 0; var not_x = if (!lte) element_width else 0; copy(dest_head + x, right_head); @@ -1979,7 +1991,7 @@ fn partial_forward_merge( left_head += element_width; dest_head += 2 * element_width; - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); } // Deal with tail. @@ -1989,7 +2001,7 @@ fn partial_forward_merge( if (data_is_owned) { inc_n_data(cmp_data, 1); } - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); } while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { copy(dest_head, left_head); @@ -2013,26 +2025,27 @@ fn partial_forward_merge_right_head_2( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) bool { - if (compare_inc(cmp, cmp_data, left_head.*, right_head.* + element_width, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, left_head.*, right_head.* + element_width, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..2) |_| { copy(dest.*, right_head.*); dest.* += element_width; right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } - if (compare_inc(cmp, cmp_data, left_head.* + element_width, right_head.*, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, left_head.* + element_width, right_head.*, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..2) |_| { copy(dest.*, left_head.*); dest.* += element_width; left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } @@ -2051,26 +2064,27 @@ fn partial_forward_merge_left_head_2( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) bool { - if (compare_inc(cmp, cmp_data, left_head.* + element_width, right_head.*, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, left_head.* + element_width, right_head.*, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..2) |_| { copy(dest.*, left_head.*); dest.* += element_width; left_head.* += element_width; } if (@intFromPtr(left_head.*) < @intFromPtr(left_tail.*) - element_width) { - return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_left_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } - if (compare_inc(cmp, cmp_data, left_head.*, right_head.* + element_width, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, left_head.*, right_head.* + element_width, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..2) |_| { copy(dest.*, right_head.*); dest.* += element_width; right_head.* += element_width; } if (@intFromPtr(right_head.*) < @intFromPtr(right_tail.*) - element_width) { - return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + return partial_forward_merge_right_head_2(dest, left_head, left_tail, right_head, right_tail, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return true; } @@ -2087,17 +2101,17 @@ test "tail_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tail_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - tail_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tail_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - tail_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tail_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -2113,22 +2127,22 @@ test "partial_backwards_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; - partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_backwards_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -2157,7 +2171,7 @@ test "partial_backwards_merge" { for (0..16) |i| { arr[i + 48] = @intCast(i + 33); } - partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); @@ -2177,7 +2191,7 @@ test "partial_backwards_merge" { arr[16] = 33; arr[63] = 49; - partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_backwards_merge(arr_ptr, 64, swap_ptr, 64, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -2193,22 +2207,22 @@ test "partial_forward_merge" { var swap_ptr = @as([*]u8, @ptrCast(&swap[0])); arr = [10]i64{ 3, 4, 5, 6, 7, 8, 1, 2, 9, 10 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 2, 4, 6, 8, 9, 10, 1, 3, 5, 7 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 6, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 3, 4, 5, 6, 8, 9, 10, 7 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_forward_merge(arr_ptr, 10, swap_ptr, 10, 9, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); arr = [10]i64{ 1, 2, 4, 5, 6, 8, 9, 3, 7, 10 }; - partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + partial_forward_merge(arr_ptr, 10, swap_ptr, 9, 7, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -2230,6 +2244,7 @@ fn quad_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) usize { const end_ptr = array + len * element_width; var current_block_len = block_len * 4; @@ -2237,7 +2252,7 @@ fn quad_merge( while (current_block_len <= len and current_block_len <= swap_len) : (current_block_len *= 4) { var arr_ptr = array; while (true) { - quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + quad_merge_block(arr_ptr, swap, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); arr_ptr += current_block_len * element_width; if (@intFromPtr(arr_ptr) + current_block_len * element_width > @intFromPtr(end_ptr)) @@ -2245,10 +2260,10 @@ fn quad_merge( } const rem_len = (@intFromPtr(end_ptr) - @intFromPtr(arr_ptr)) / element_width; - tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_merge(arr_ptr, rem_len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } - tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_merge(array, len, swap, swap_len, current_block_len / 4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return current_block_len / 2; } @@ -2264,6 +2279,7 @@ fn quad_merge_block( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { const block_x_2 = 2 * block_len; @@ -2276,23 +2292,23 @@ fn quad_merge_block( if (data_is_owned) { inc_n_data(cmp_data, 2); } - const in_order_1_2: u2 = @intFromBool(compare(cmp, cmp_data, block2 - element_width, block2) != GT); - const in_order_3_4: u2 = @intFromBool(compare(cmp, cmp_data, block4 - element_width, block4) != GT); + const in_order_1_2: u2 = @intFromBool(compare(cmp, cmp_data, block2 - element_width, block2, indirect) != GT); + const in_order_3_4: u2 = @intFromBool(compare(cmp, cmp_data, block4 - element_width, block4, indirect) != GT); switch (in_order_1_2 | (in_order_3_4 << 1)) { 0 => { // Nothing sorted. Just run merges on both. - cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 1 => { // First half sorted already. @memcpy(swap[0..(element_width * block_x_2)], array[0..(element_width * block_x_2)]); - cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap + block_x_2 * element_width, block3, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 2 => { // Second half sorted already. - cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(swap, array, block_len, block_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); @memcpy((swap + element_width * block_x_2)[0..(element_width * block_x_2)], block3[0..(element_width * block_x_2)]); }, 3 => { @@ -2300,7 +2316,7 @@ fn quad_merge_block( if (data_is_owned) { inc_n_data(cmp_data, 1); } - const in_order_2_3 = compare(cmp, cmp_data, block3 - element_width, block3) != GT; + const in_order_2_3 = compare(cmp, cmp_data, block3 - element_width, block3, indirect) != GT; if (in_order_2_3) // Lucky, all sorted. return; @@ -2311,7 +2327,7 @@ fn quad_merge_block( } // Merge 2 larger blocks. - cross_merge(array, swap, block_x_2, block_x_2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + cross_merge(array, swap, block_x_2, block_x_2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } /// Cross merge attempts to merge two arrays in chunks of multiple elements. @@ -2326,6 +2342,7 @@ fn cross_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { var left_head = src; var right_head = src + left_len * element_width; @@ -2337,8 +2354,8 @@ fn cross_merge( if (left_len + 1 >= right_len and right_len + 1 >= left_len and left_len >= 32) { const offset = 15 * element_width; // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, left_head + offset, right_head, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, left_head, right_head + offset, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, left_tail, right_tail - offset, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, left_tail - offset, right_tail, data_is_owned, inc_n_data) != GT) { - parity_merge(dest, src, left_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + if (compare_inc(cmp, cmp_data, left_head + offset, right_head, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, left_head, right_head + offset, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, left_tail, right_tail - offset, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, left_tail - offset, right_tail, data_is_owned, inc_n_data, indirect) != GT) { + parity_merge(dest, src, left_len, right_len, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } } @@ -2351,7 +2368,7 @@ fn cross_merge( if (@as(isize, @intCast(@intFromPtr(left_tail))) - @as(isize, @intCast(@intFromPtr(left_head))) > @as(isize, @intCast(8 * element_width))) { // 8 elements all less than or equal to and can be moved together. // Due to looping, these must use `compare_inc` - while (compare_inc(cmp, cmp_data, left_head + 7 * element_width, right_head, data_is_owned, inc_n_data) != GT) { + while (compare_inc(cmp, cmp_data, left_head + 7 * element_width, right_head, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..8) |_| { copy(dest_head, left_head); dest_head += element_width; @@ -2364,7 +2381,7 @@ fn cross_merge( // Attempt to do the same from the tail. // 8 elements all greater than and can be moved together. // Due to looping, these must use `compare_inc` - while (compare_inc(cmp, cmp_data, left_tail - 7 * element_width, right_tail, data_is_owned, inc_n_data) == GT) { + while (compare_inc(cmp, cmp_data, left_tail - 7 * element_width, right_tail, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..8) |_| { copy(dest_tail, left_tail); dest_tail -= element_width; @@ -2380,7 +2397,7 @@ fn cross_merge( if (@as(isize, @intCast(@intFromPtr(right_tail))) - @as(isize, @intCast(@intFromPtr(right_head))) > @as(isize, @intCast(8 * element_width))) { // left greater than 8 elements right and can be moved together. // Due to looping, these must use `compare_inc` - while (compare_inc(cmp, cmp_data, left_head, right_head + 7 * element_width, data_is_owned, inc_n_data) == GT) { + while (compare_inc(cmp, cmp_data, left_head, right_head + 7 * element_width, data_is_owned, inc_n_data, indirect) == GT) { inline for (0..8) |_| { copy(dest_head, right_head); dest_head += element_width; @@ -2393,7 +2410,7 @@ fn cross_merge( // Attempt to do the same from the tail. // left less than or equalt to 8 elements right and can be moved together. // Due to looping, these must use `compare_inc` - while (compare_inc(cmp, cmp_data, left_tail, right_tail - 7 * element_width, data_is_owned, inc_n_data) != GT) { + while (compare_inc(cmp, cmp_data, left_tail, right_tail - 7 * element_width, data_is_owned, inc_n_data, indirect) != GT) { inline for (0..8) |_| { copy(dest_tail, right_tail); dest_tail -= element_width; @@ -2413,8 +2430,8 @@ fn cross_merge( inc_n_data(cmp_data, 16); } for (0..8) |_| { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy, indirect); } } @@ -2425,7 +2442,7 @@ fn cross_merge( if (data_is_owned) { inc_n_data(cmp_data, 1); } - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); } while (@intFromPtr(left_head) <= @intFromPtr(left_tail)) { copy(dest_head, left_head); @@ -2450,32 +2467,32 @@ test "quad_merge" { var size: usize = undefined; arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 2, 9, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 16); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + size = quad_merge(arr_ptr, 9, swap_ptr, 9, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 16); arr = [10]i64{ 3, 4, 6, 9, 1, 2, 5, 10, 7, 8 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + size = quad_merge(arr_ptr, 10, swap_ptr, 10, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); try testing.expectEqual(size, 8); // Limited swap, can't finish merge arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + size = quad_merge(arr_ptr, 10, swap_ptr, 4, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [10]i64{ 1, 3, 4, 5, 6, 7, 8, 9, 2, 10 }); try testing.expectEqual(size, 4); arr = [10]i64{ 7, 8, 5, 6, 3, 4, 1, 9, 2, 10 }; - size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + size = quad_merge(arr_ptr, 10, swap_ptr, 3, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [10]i64{ 5, 6, 7, 8, 1, 3, 4, 9, 2, 10 }); try testing.expectEqual(size, 4); @@ -2492,32 +2509,32 @@ test "quad_merge_block" { // case 0 - totally unsorted arr = [8]i64{ 7, 8, 5, 6, 3, 4, 1, 2 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 1 - first half sorted arr = [8]i64{ 5, 6, 7, 8, 3, 4, 1, 2 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 2 - second half sorted arr = [8]i64{ 7, 8, 5, 6, 1, 2, 3, 4 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 3 both haves sorted arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); // TODO: fix // try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 3 - lucky, sorted arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; - quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); // try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -2541,7 +2558,7 @@ test "cross_merge" { for (0..32) |i| { src[i + 32] = @intCast(i + 1); } - cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); @@ -2550,7 +2567,7 @@ test "cross_merge" { src[i * 2] = @intCast(i * 2 + 1); src[i * 2 + 1] = @intCast(i * 2 + 2); } - cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); @@ -2561,7 +2578,7 @@ test "cross_merge" { for (0..44) |i| { src[i + 20] = @intCast(i + 1); } - cross_merge(dest_ptr, src_ptr, 20, 44, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + cross_merge(dest_ptr, src_ptr, 20, 44, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); @@ -2578,7 +2595,7 @@ test "cross_merge" { for (0..16) |i| { src[i + 48] = @intCast(i + 33); } - cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + cross_merge(dest_ptr, src_ptr, 32, 32, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, expected); } @@ -2600,6 +2617,7 @@ fn quad_swap( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) QuadSwapResult { // TODO: This is a solid amount of stack space. Is that ok? // That said, it only ever allocates once (not recursive). @@ -2622,10 +2640,10 @@ fn quad_swap( if (data_is_owned) { inc_n_data(cmp_data, 4); } - var v1: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) == GT); - var v2: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) == GT); - var v3: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) == GT); - var v4: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) == GT); + var v1: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width, indirect) == GT); + var v2: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, indirect) == GT); + var v3: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width, indirect) == GT); + var v4: u4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width, indirect) == GT); // This is an attempt at computed gotos in zig. // Not yet sure if it will optimize as well as the raw gotos in C. @@ -2635,14 +2653,14 @@ fn quad_swap( 0 => { // potentially already ordered, check rest! // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) != GT) { break :switch_state .ordered; } // 16 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 16); } - quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 8 * element_width; continue :outer; @@ -2650,7 +2668,7 @@ fn quad_swap( 15 => { // potentially already reverse ordered, check rest! // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) == GT) { reverse_head = arr_ptr; break :switch_state .reversed; } @@ -2678,7 +2696,7 @@ fn quad_swap( if (data_is_owned) { inc_n_data(cmp_data, 16); } - quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 8 * element_width; continue :outer; @@ -2693,14 +2711,14 @@ fn quad_swap( if (data_is_owned) { inc_n_data(cmp_data, 4); } - v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) == GT); - v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) == GT); - v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) == GT); - v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) == GT); + v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width, indirect) == GT); + v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, indirect) == GT); + v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width, indirect) == GT); + v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width, indirect) == GT); if (v1 | v2 | v3 | v4 != 0) { // Sadly not ordered still, maybe reversed though? // Due to short circuit logic, these must use `compare_inc` - if (v1 + v2 + v3 + v4 == 4 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { + if (v1 + v2 + v3 + v4 == 4 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) == GT) { reverse_head = arr_ptr; state = .reversed; continue; @@ -2709,7 +2727,7 @@ fn quad_swap( continue; } // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) != GT) { state = .ordered; continue; } @@ -2718,7 +2736,7 @@ fn quad_swap( if (data_is_owned) { inc_n_data(cmp_data, 16); } - quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 8 * element_width; continue :outer; } @@ -2734,17 +2752,17 @@ fn quad_swap( if (data_is_owned) { inc_n_data(cmp_data, 4); } - v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width) != GT); - v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT); - v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width) != GT); - v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width) != GT); + v1 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width, indirect) != GT); + v2 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, indirect) != GT); + v3 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width, indirect) != GT); + v4 = @intFromBool(compare(cmp, cmp_data, arr_ptr + 6 * element_width, arr_ptr + 7 * element_width, indirect) != GT); if (v1 | v2 | v3 | v4 != 0) { // Sadly not still reversed. // So we just need to reverse upto this point, but not the current 8 element block. } else { // This also checks the boundary between this and the last block. // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) == GT) { // Row multiple reversed blocks in a row! state = .reversed; continue; @@ -2755,12 +2773,12 @@ fn quad_swap( // Since we already have v1 to v4, check the next block state. // Due to short circuit logic, these must use `compare_inc` - if (v1 + v2 + v3 + v4 == 4 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) { + if (v1 + v2 + v3 + v4 == 4 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) != GT) { state = .ordered; continue; } // Due to short circuit logic, these must use `compare_inc` - if (v1 + v2 + v3 + v4 == 0 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { + if (v1 + v2 + v3 + v4 == 0 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) == GT and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) == GT) { reverse_head = arr_ptr; state = .reversed; continue; @@ -2778,12 +2796,12 @@ fn quad_swap( arr_ptr -= 8 * element_width; // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) == GT or compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) == GT or compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) == GT) { + if (compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) == GT or compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) == GT or compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) == GT) { // 16 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 16); } - quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy); + quad_swap_merge(arr_ptr, swap, cmp, cmp_data, element_width, copy, indirect); } arr_ptr += 8 * element_width; continue :outer; @@ -2793,19 +2811,19 @@ fn quad_swap( const rem = len % 8; reverse_block: { // Due to chance of breaking and not running, must use `comapare_inc`. - if (rem == 7 and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data) != GT) + if (rem == 7 and compare_inc(cmp, cmp_data, arr_ptr + 5 * element_width, arr_ptr + 6 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; - if (rem >= 6 and compare_inc(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width, data_is_owned, inc_n_data) != GT) + if (rem >= 6 and compare_inc(cmp, cmp_data, arr_ptr + 4 * element_width, arr_ptr + 5 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; - if (rem >= 5 and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data) != GT) + if (rem >= 5 and compare_inc(cmp, cmp_data, arr_ptr + 3 * element_width, arr_ptr + 4 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; - if (rem >= 4 and compare_inc(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, data_is_owned, inc_n_data) != GT) + if (rem >= 4 and compare_inc(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; - if (rem >= 3 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data) != GT) + if (rem >= 3 and compare_inc(cmp, cmp_data, arr_ptr + 1 * element_width, arr_ptr + 2 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; - if (rem >= 2 and compare_inc(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width, data_is_owned, inc_n_data) != GT) + if (rem >= 2 and compare_inc(cmp, cmp_data, arr_ptr + 0 * element_width, arr_ptr + 1 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; - if (rem >= 1 and compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width, data_is_owned, inc_n_data) != GT) + if (rem >= 1 and compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr + 0 * element_width, data_is_owned, inc_n_data, indirect) != GT) break :reverse_block; quad_reversal(reverse_head, arr_ptr + rem * element_width - element_width, element_width, copy); @@ -2824,7 +2842,7 @@ fn quad_swap( } } if (!skip_tail_swap) { - tail_swap(arr_ptr, len % 8, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_swap(arr_ptr, len % 8, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } // Group into 32 element blocks. @@ -2836,19 +2854,19 @@ fn quad_swap( arr_ptr += 32 * element_width; }) { // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, arr_ptr + 7 * element_width, arr_ptr + 8 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 15 * element_width, arr_ptr + 16 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr + 23 * element_width, arr_ptr + 24 * element_width, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, arr_ptr + 7 * element_width, arr_ptr + 8 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 15 * element_width, arr_ptr + 16 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr + 23 * element_width, arr_ptr + 24 * element_width, data_is_owned, inc_n_data, indirect) != GT) { // Already in order. continue; } - parity_merge(swap, arr_ptr, 8, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - parity_merge(arr_ptr, swap, 16, 16, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_merge(swap, arr_ptr, 8, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + parity_merge(swap + 16 * element_width, arr_ptr + 16 * element_width, 8, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + parity_merge(arr_ptr, swap, 16, 16, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } // Deal with final tail for 32 element blocks. // Anything over 8 elements is multiple blocks worth merging together. if (len % 32 > 8) { - tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_merge(arr_ptr, len % 32, swap, 32, 8, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } return .unfinished; @@ -2863,11 +2881,12 @@ fn quad_swap_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { - parity_merge_two(swap, array, cmp, cmp_data, element_width, copy); - parity_merge_two(swap + 4 * element_width, array + 4 * element_width, cmp, cmp_data, element_width, copy); + parity_merge_two(swap, array, cmp, cmp_data, element_width, copy, indirect); + parity_merge_two(swap + 4 * element_width, array + 4 * element_width, cmp, cmp_data, element_width, copy, indirect); - parity_merge_four(array, swap, cmp, cmp_data, element_width, copy); + parity_merge_four(array, swap, cmp, cmp_data, element_width, copy, indirect); } /// Reverse values from start to end. @@ -2945,7 +2964,7 @@ test "quad_swap" { 72, 58, 57, }; - var result = quad_swap(arr_ptr, 75, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + var result = quad_swap(arr_ptr, 75, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(result, .unfinished); try testing.expectEqual(arr, [75]i64{ @@ -2970,7 +2989,7 @@ test "quad_swap" { expected[i] = @intCast(i + 1); arr[i] = @intCast(75 - i); } - result = quad_swap(arr_ptr, 75, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + result = quad_swap(arr_ptr, 75, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(result, .sorted); try testing.expectEqual(arr, expected); @@ -2984,17 +3003,17 @@ test "quad_swap_merge" { arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 7, 1, 3, 6, 8, 2, 4 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 8, 3, 4, 5, 6, 2, 7 }; swap = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + quad_swap_merge(arr_ptr, swap_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(arr, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } @@ -3030,9 +3049,10 @@ fn tail_swap( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { if (len < 8) { - tiny_sort(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tiny_sort(array, len, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); return; } @@ -3044,22 +3064,22 @@ fn tail_swap( const quad4 = half2 - quad3; var arr_ptr = array; - tail_swap(arr_ptr, quad1, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_swap(arr_ptr, quad1, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); arr_ptr += quad1 * element_width; - tail_swap(arr_ptr, quad2, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_swap(arr_ptr, quad2, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); arr_ptr += quad2 * element_width; - tail_swap(arr_ptr, quad3, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_swap(arr_ptr, quad3, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); arr_ptr += quad3 * element_width; - tail_swap(arr_ptr, quad4, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + tail_swap(arr_ptr, quad4, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); // Due to short circuit logic, these must use `compare_inc` - if (compare_inc(cmp, cmp_data, array + (quad1 - 1) * element_width, array + quad1 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, array + (half1 - 1) * element_width, array + half1 * element_width, data_is_owned, inc_n_data) != GT and compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr, data_is_owned, inc_n_data) != GT) { + if (compare_inc(cmp, cmp_data, array + (quad1 - 1) * element_width, array + quad1 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, array + (half1 - 1) * element_width, array + half1 * element_width, data_is_owned, inc_n_data, indirect) != GT and compare_inc(cmp, cmp_data, arr_ptr - 1 * element_width, arr_ptr, data_is_owned, inc_n_data, indirect) != GT) { return; } - parity_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); - parity_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_merge(swap, array, quad1, quad2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + parity_merge(swap + half1 * element_width, array + half1 * element_width, quad3, quad4, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); + parity_merge(array, swap, half1, half2, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); } /// Merges two neighboring sorted arrays into dest. @@ -3075,6 +3095,7 @@ fn parity_merge( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { std.debug.assert(left_len == right_len or left_len == right_len - 1 or left_len - 1 == right_len); @@ -3091,20 +3112,20 @@ fn parity_merge( if (data_is_owned) { inc_n_data(cmp_data, 1); } - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); } // 2 + 2(left_len -1) = (2*left_len) guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 2 * left_len); } - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); for (0..(left_len - 1)) |_| { - head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); + head_branchless_merge(&dest_head, &left_head, &right_head, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy, indirect); } - tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy); + tail_branchless_merge(&dest_tail, &left_tail, &right_tail, cmp, cmp_data, element_width, copy, indirect); } test "tail_swap" { @@ -3124,7 +3145,7 @@ test "tail_swap" { var rng = std.rand.DefaultPrng.init(seed); rng.random().shuffle(i64, arr[0..]); - tail_swap(arr_ptr, 31, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tail_swap(arr_ptr, 31, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); } @@ -3141,13 +3162,13 @@ test "parity_merge" { arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + parity_merge(dest_ptr, arr_ptr, 4, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } @@ -3160,25 +3181,25 @@ test "parity_merge" { arr = [9]i64{ 1, 3, 5, 8, 2, 4, 6, 7, 9 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); arr = [9]i64{ 6, 7, 8, 9, 1, 2, 3, 4, 5 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + parity_merge(dest_ptr, arr_ptr, 4, 5, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); arr = [9]i64{ 1, 3, 5, 7, 8, 2, 4, 6, 9 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 5, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + parity_merge(dest_ptr, arr_ptr, 5, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); arr = [9]i64{ 5, 6, 7, 8, 9, 1, 2, 3, 4 }; dest = [9]i64{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge(dest_ptr, arr_ptr, 5, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + parity_merge(dest_ptr, arr_ptr, 5, 4, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(dest, [9]i64{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }); } @@ -3198,6 +3219,7 @@ fn tiny_sort( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { std.debug.assert(len < 8); @@ -3213,7 +3235,7 @@ fn tiny_sort( if (data_is_owned) { inc_n_data(cmp_data, 1); } - swap_branchless(array, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(array, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); }, 3 => { // 3 guaranteed compares. @@ -3221,23 +3243,23 @@ fn tiny_sort( inc_n_data(cmp_data, 3); } var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); }, 4 => { - parity_swap_four(array, tmp_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_swap_four(array, tmp_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 5 => { - parity_swap_five(array, tmp_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_swap_five(array, tmp_ptr, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 6 => { - parity_swap_six(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_swap_six(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, 7 => { - parity_swap_seven(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data); + parity_swap_seven(array, tmp_ptr, swap, cmp, cmp_data, element_width, copy, data_is_owned, inc_n_data, indirect); }, else => { unreachable; @@ -3254,18 +3276,19 @@ fn parity_swap_four( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { // 3 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 3); } var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; - const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width, indirect) == GT; if (gt) { // 3 guaranteed compares. if (data_is_owned) { @@ -3275,11 +3298,11 @@ fn parity_swap_four( copy(arr_ptr, arr_ptr + element_width); copy(arr_ptr + element_width, tmp_ptr); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); } } @@ -3292,19 +3315,20 @@ fn parity_swap_five( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { // 4 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 4); } var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; - var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr = array; if (more_work != 0) { @@ -3312,17 +3336,17 @@ fn parity_swap_five( if (data_is_owned) { inc_n_data(cmp_data, 6); } - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); } } @@ -3336,31 +3360,32 @@ fn parity_swap_six( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { // 7 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 5); } var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 3 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr = array; { - const lte = compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width) != GT; + const lte = compare(cmp, cmp_data, arr_ptr + 2 * element_width, arr_ptr + 3 * element_width, indirect) != GT; if (lte) { // 2 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 2); } - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 4 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); return; } } @@ -3370,7 +3395,7 @@ fn parity_swap_six( inc_n_data(cmp_data, 8); } { - const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width, indirect) == GT; var x = if (gt) element_width else 0; var not_x = if (!gt) element_width else 0; copy(swap, arr_ptr + x); @@ -3379,7 +3404,7 @@ fn parity_swap_six( arr_ptr += 4 * element_width; } { - const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width, indirect) == GT; var x = if (gt) element_width else 0; var not_x = if (!gt) element_width else 0; copy(swap + 4 * element_width, arr_ptr + x); @@ -3391,17 +3416,17 @@ fn parity_swap_six( var left = swap; var right = swap + 3 * element_width; - head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); arr_ptr = array + 5 * element_width; left = swap + 2 * element_width; right = swap + 5 * element_width; - tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - const gt = compare(cmp, cmp_data, left, right) == GT; + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + const gt = compare(cmp, cmp_data, left, right, indirect) == GT; const from = if (gt) left else right; copy(arr_ptr, from); } @@ -3416,23 +3441,24 @@ fn parity_swap_seven( copy: CopyFn, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) void { // 6 guaranteed compares. if (data_is_owned) { inc_n_data(cmp_data, 6); } var arr_ptr = array; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= 3 * element_width; - var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + var more_work = swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr += 2 * element_width; - more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + more_work += swap_branchless_return_gt(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr -= element_width; if (more_work == 0) @@ -3442,11 +3468,11 @@ fn parity_swap_seven( if (data_is_owned) { inc_n_data(cmp_data, 11); } - swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy); + swap_branchless(arr_ptr, tmp_ptr, cmp, cmp_data, element_width, copy, indirect); arr_ptr = array; { - const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width, indirect) == GT; var x = if (gt) element_width else 0; var not_x = if (!gt) element_width else 0; copy(swap, arr_ptr + x); @@ -3455,7 +3481,7 @@ fn parity_swap_seven( arr_ptr += 3 * element_width; } { - const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width, indirect) == GT; var x = if (gt) element_width else 0; var not_x = if (!gt) element_width else 0; copy(swap + 3 * element_width, arr_ptr + x); @@ -3463,7 +3489,7 @@ fn parity_swap_seven( arr_ptr += 2 * element_width; } { - const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, arr_ptr, arr_ptr + element_width, indirect) == GT; var x = if (gt) element_width else 0; var not_x = if (!gt) element_width else 0; copy(swap + 5 * element_width, arr_ptr + x); @@ -3474,18 +3500,18 @@ fn parity_swap_seven( var left = swap; var right = swap + 3 * element_width; - head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + head_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); arr_ptr = array + 6 * element_width; left = swap + 2 * element_width; right = swap + 6 * element_width; - tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy); - const gt = compare(cmp, cmp_data, left, right) == GT; + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&arr_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + const gt = compare(cmp, cmp_data, left, right, indirect) == GT; const from = if (gt) left else right; copy(arr_ptr, from); } @@ -3500,12 +3526,12 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [7]i64{ 3, 1, 2, 5, 4, 7, 6 }; - tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); arr = [7]i64{ 7, 6, 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 7, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [7]i64{ 1, 2, 3, 4, 5, 6, 7 }); } @@ -3514,12 +3540,12 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [6]i64{ 3, 1, 2, 6, 4, 5 }; - tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); arr = [6]i64{ 6, 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 6, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [6]i64{ 1, 2, 3, 4, 5, 6 }); } @@ -3528,12 +3554,12 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [5]i64{ 2, 1, 4, 3, 5 }; - tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); arr = [5]i64{ 5, 4, 3, 2, 1 }; - tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 5, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [5]i64{ 1, 2, 3, 4, 5 }); } @@ -3542,26 +3568,26 @@ test "tiny_sort" { var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); arr = [4]i64{ 4, 2, 1, 3 }; - tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 1, 4, 3 }; - tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 4, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [4]i64{ 1, 2, 3, 4 }); } { var arr = [3]i64{ 2, 3, 1 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - tiny_sort(arr_ptr, 3, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 3, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [3]i64{ 1, 2, 3 }); } { var arr = [2]i64{ 2, 1 }; var arr_ptr = @as([*]u8, @ptrCast(&arr[0])); - tiny_sort(arr_ptr, 2, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data); + tiny_sort(arr_ptr, 2, swap_ptr, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, [2]i64{ 1, 2 }); } @@ -3581,24 +3607,25 @@ inline fn parity_merge_four( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { var left = array; var right = array + (4 * element_width); var dest_ptr = dest; - head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - const lte = compare(cmp, cmp_data, left, right) != GT; + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + const lte = compare(cmp, cmp_data, left, right, indirect) != GT; var to_copy = if (lte) left else right; copy(dest_ptr, to_copy); left = array + (3 * element_width); right = array + (7 * element_width); dest_ptr = dest + (7 * element_width); - tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - const gt = compare(cmp, cmp_data, left, right) == GT; + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + const gt = compare(cmp, cmp_data, left, right, indirect) == GT; to_copy = if (gt) left else right; copy(dest_ptr, to_copy); } @@ -3612,20 +3639,21 @@ inline fn parity_merge_two( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { var left = array; var right = array + (2 * element_width); var dest_ptr = dest; - head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - const lte = compare(cmp, cmp_data, left, right) != GT; + head_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + const lte = compare(cmp, cmp_data, left, right, indirect) != GT; var to_copy = if (lte) left else right; copy(dest_ptr, to_copy); left = array + element_width; right = array + (3 * element_width); dest_ptr = dest + (3 * element_width); - tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy); - const gt = compare(cmp, cmp_data, left, right) == GT; + tail_branchless_merge(&dest_ptr, &left, &right, cmp, cmp_data, element_width, copy, indirect); + const gt = compare(cmp, cmp_data, left, right, indirect) == GT; to_copy = if (gt) left else right; copy(dest_ptr, to_copy); } @@ -3643,11 +3671,12 @@ inline fn head_branchless_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { // Note equivalent c code: // *ptd++ = cmp(ptl, ptr) <= 0 ? *ptl++ : *ptr++; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - const lte = compare(cmp, cmp_data, left.*, right.*) != GT; + const lte = compare(cmp, cmp_data, left.*, right.*, indirect) != GT; const from = if (lte) left else right; copy(dest.*, from.*); from.* += element_width; @@ -3667,11 +3696,12 @@ inline fn tail_branchless_merge( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { // Note equivalent c code: // *tpd-- = cmp(tpl, tpr) > 0 ? *tpl-- : *tpr--; // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - const gt = compare(cmp, cmp_data, left.*, right.*) == GT; + const gt = compare(cmp, cmp_data, left.*, right.*, indirect) == GT; const from = if (gt) left else right; copy(dest.*, from.*); from.* -= element_width; @@ -3687,9 +3717,10 @@ inline fn swap_branchless( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) void { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - _ = swap_branchless_return_gt(ptr, tmp, cmp, cmp_data, element_width, copy); + _ = swap_branchless_return_gt(ptr, tmp, cmp, cmp_data, element_width, copy, indirect); } /// Requires that the refcount of cmp_data be incremented 1 time. @@ -3700,9 +3731,10 @@ inline fn swap_branchless_return_gt( cmp_data: Opaque, element_width: usize, copy: CopyFn, + comptime indirect: bool, ) u8 { // While not guaranteed branchless, tested in godbolt for x86_64, aarch32, aarch64, riscv64, and wasm32. - const gt = compare(cmp, cmp_data, ptr, ptr + element_width) == GT; + const gt = compare(cmp, cmp_data, ptr, ptr + element_width, indirect) == GT; var x = if (gt) element_width else 0; const from = if (gt) ptr else ptr + element_width; copy(tmp, from); @@ -3712,8 +3744,22 @@ inline fn swap_branchless_return_gt( } /// Requires that the refcount of cmp_data be incremented 1 time. -inline fn compare(cmp: CompareFn, cmp_data: Opaque, lhs: [*]u8, rhs: [*]u8) Ordering { - return @as(Ordering, @enumFromInt(cmp(cmp_data, lhs, rhs))); +inline fn compare( + cmp: CompareFn, + cmp_data: Opaque, + lhs_opaque: *anyopaque, + rhs_opaque: *anyopaque, + comptime indirect: bool, +) Ordering { + if (indirect) { + const lhs = @as(*[*]u8, @ptrCast(@alignCast(lhs_opaque))).*; + const rhs = @as(*[*]u8, @ptrCast(@alignCast(rhs_opaque))).*; + return @as(Ordering, @enumFromInt(cmp(cmp_data, lhs, rhs))); + } else { + const lhs = @as([*]u8, @ptrCast(@alignCast(lhs_opaque))); + const rhs = @as([*]u8, @ptrCast(@alignCast(rhs_opaque))); + return @as(Ordering, @enumFromInt(cmp(cmp_data, lhs, rhs))); + } } /// Only use this as a last resort. @@ -3727,11 +3773,12 @@ inline fn compare_inc( rhs: [*]u8, comptime data_is_owned: bool, inc_n_data: IncN, + comptime indirect: bool, ) Ordering { if (data_is_owned) { inc_n_data(cmp_data, 1); } - return compare(cmp, cmp_data, lhs, rhs); + return compare(cmp, cmp_data, lhs, rhs, indirect); } test "parity_merge_four" { @@ -3742,17 +3789,17 @@ test "parity_merge_four" { arr = [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 5, 6, 7, 8, 1, 2, 3, 4 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; dest = [8]i64{ 0, 0, 0, 0, 0, 0, 0, 0 }; - parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_four(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [8]i64{ 1, 2, 3, 4, 5, 6, 7, 8 }); } @@ -3764,27 +3811,27 @@ test "parity_merge_two" { arr = [4]i64{ 1, 2, 3, 4 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 3, 2, 4 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 3, 4, 1, 2 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 2, 4, 1, 3 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); arr = [4]i64{ 1, 4, 2, 3 }; dest = [4]i64{ 0, 0, 0, 0 }; - parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + parity_merge_two(dest_ptr, arr_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [4]i64{ 1, 2, 3, 4 }); } @@ -3796,12 +3843,12 @@ test "head_branchless_merge" { var left_ptr = @as([*]u8, @ptrCast(&left[0])); var right_ptr = @as([*]u8, @ptrCast(&right[0])); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + head_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); } @@ -3814,12 +3861,12 @@ test "tail_branchless_merge" { var left_ptr = @as([*]u8, @ptrCast(&left[left.len - 1])); var right_ptr = @as([*]u8, @ptrCast(&right[right.len - 1])); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); - tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); + tail_branchless_merge(&dest_ptr, &left_ptr, &right_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(dest, [6]i64{ 1, 2, 2, 7, 8, 10 }); } @@ -3831,26 +3878,18 @@ test "swap" { var tmp_ptr = @as([*]u8, @ptrCast(&tmp)); arr = [2]i64{ 10, 20 }; - swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(arr, [2]i64{ 10, 20 }); arr = [2]i64{ 77, -12 }; - swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(arr, [2]i64{ -12, 77 }); arr = [2]i64{ -22, -22 }; - swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy); + swap_branchless(arr_ptr, tmp_ptr, &test_i64_compare, null, @sizeOf(i64), &test_i64_copy, false); try testing.expectEqual(arr, [2]i64{ -22, -22 }); } -// While I think it is technically safe, I'm not a fan of using a threadlocal for this. -threadlocal var inner_cmp: ?CompareFn = null; -pub fn indirect_compare(compare_data: Opaque, lhs_ptr: Opaque, rhs_ptr: Opaque) callconv(.C) u8 { - const lhs = @as(*[*]u8, @ptrCast(@alignCast(lhs_ptr))).*; - const rhs = @as(*[*]u8, @ptrCast(@alignCast(rhs_ptr))).*; - return (inner_cmp.?)(compare_data, lhs, rhs); -} - pub fn pointer_copy(dst_ptr: Opaque, src_ptr: Opaque) callconv(.C) void { @as(*usize, @alignCast(@ptrCast(dst_ptr))).* = @as(*usize, @alignCast(@ptrCast(src_ptr))).*; } From e69532d3b344f8a7a443fcc3c8e1ba0631281946 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 28 Jul 2024 18:54:03 -0700 Subject: [PATCH 121/203] update tuning todo --- crates/compiler/builtins/bitcode/src/sort.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/sort.zig b/crates/compiler/builtins/bitcode/src/sort.zig index 60ad645d3de..a02bb4b4662 100644 --- a/crates/compiler/builtins/bitcode/src/sort.zig +++ b/crates/compiler/builtins/bitcode/src/sort.zig @@ -14,7 +14,7 @@ const CopyFn = *const fn (Opaque, Opaque) callconv(.C) void; const IncN = *const fn (?[*]u8, usize) callconv(.C) void; /// Any size larger than the max element buffer will be sorted indirectly via pointers. -/// TODO: tune this. +/// TODO: tune this. I think due to llvm inlining the compare, the value likely should be lower. /// I did some basic basic testing on my M1 and x86 machines with the c version of fluxsort. /// The best tradeoff point is not the clearest and heavily depends on machine specifics. /// Generally speaking, the faster memcpy is and the larger the cache line, the larger this should be. @@ -2528,8 +2528,7 @@ test "quad_merge_block" { // case 3 both haves sorted arr = [8]i64{ 1, 3, 5, 7, 2, 4, 6, 8 }; quad_merge_block(arr_ptr, swap_ptr, 2, &test_i64_compare_refcounted, @ptrCast(&test_count), @sizeOf(i64), &test_i64_copy, true, &test_inc_n_data, false); - // TODO: fix - // try testing.expectEqual(test_count, 0); + try testing.expectEqual(test_count, 0); try testing.expectEqual(arr, expected); // case 3 - lucky, sorted From 4e03b5179f1d2cd567ec66b051efe33465d81765 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Mon, 29 Jul 2024 14:53:24 +1000 Subject: [PATCH 122/203] bundle `.lib` files when making a package --- crates/packaging/src/tarball.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/packaging/src/tarball.rs b/crates/packaging/src/tarball.rs index 51cfabcfc04..088f1081fc0 100644 --- a/crates/packaging/src/tarball.rs +++ b/crates/packaging/src/tarball.rs @@ -159,6 +159,7 @@ fn write_archive(path: &Path, writer: W) -> io::Result<()> { // legacy linker formats Some("o"), Some("a"), + Some("lib"), Some("obj"), Some("wasm"), // optimized wasm builds compile to .zig for now, From 91fdd903e7829ad12e631a1bb454761e23059f7f Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 29 Jul 2024 10:01:48 -0400 Subject: [PATCH 123/203] `comment` -> `spaces_middle` --- crates/compiler/can/src/def.rs | 2 +- crates/compiler/can/src/desugar.rs | 14 +++++++------- crates/compiler/can/src/suffixed.rs | 2 +- .../test_suffixed__suffixed_tests__basic.snap | 2 +- ...t_suffixed__suffixed_tests__closure_simple.snap | 2 +- ...__suffixed_tests__closure_with_annotations.snap | 4 ++-- ...uffixed__suffixed_tests__closure_with_defs.snap | 6 +++--- ...ixed__suffixed_tests__defs_suffixed_middle.snap | 2 +- .../test_suffixed__suffixed_tests__if_complex.snap | 6 +++--- ...ed__suffixed_tests__last_suffixed_multiple.snap | 4 ++-- ...suffixed__suffixed_tests__multi_defs_stmts.snap | 2 +- ..._suffixed__suffixed_tests__multiple_suffix.snap | 2 +- ...est_suffixed__suffixed_tests__simple_pizza.snap | 2 +- ..._suffixed__suffixed_tests__trailing_binops.snap | 2 +- ..._suffixed__suffixed_tests__type_annotation.snap | 2 +- ...st_suffixed__suffixed_tests__when_branches.snap | 2 +- crates/compiler/fmt/src/def.rs | 8 ++++---- crates/compiler/parse/src/ast.rs | 6 +++--- crates/compiler/parse/src/expr.rs | 4 ++-- crates/compiler/parse/src/remove_spaces.rs | 4 ++-- .../pass/ann_closed_union.expr.result-ast | 2 +- .../snapshots/pass/ann_open_union.expr.result-ast | 2 +- .../annotated_record_destructure.expr.result-ast | 2 +- .../pass/annotated_tag_destructure.expr.result-ast | 2 +- .../annotated_tuple_destructure.expr.result-ast | 2 +- .../pass/expect_defs.moduledefs.result-ast | 6 +++--- .../pass/fn_with_record_arg.expr.result-ast | 2 +- .../function_with_tuple_ext_type.expr.result-ast | 2 +- .../pass/function_with_tuple_type.expr.result-ast | 2 +- .../nested_def_annotation.moduledefs.result-ast | 2 +- .../snapshots/pass/tuple_type.expr.result-ast | 2 +- .../snapshots/pass/tuple_type_ext.expr.result-ast | 2 +- .../pass/type_signature_def.expr.result-ast | 2 +- .../type_signature_function_def.expr.result-ast | 2 +- .../snapshots/pass/where_ident.expr.result-ast | 2 +- 35 files changed, 56 insertions(+), 56 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 9a807e875bf..c0b4de7e297 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2969,7 +2969,7 @@ fn to_pending_value_def<'a>( AnnotatedBody { ann_pattern, ann_type, - comment: _, + spaces_middle: _, body_pattern, body_expr, } => { diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 150013b4909..b966f759cc5 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -86,13 +86,13 @@ fn desugar_value_def<'a>( AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern, body_expr, } => AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path), body_expr: desugar_expr(arena, body_expr, src, line_info, module_path), }, @@ -170,7 +170,7 @@ fn desugar_value_def<'a>( ext: None, }, )), - comment: &[], + spaces_middle: &[], body_pattern: new_pat, body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path), } @@ -235,7 +235,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern, body_expr, } => { @@ -244,7 +244,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) Ok(new_expr) => AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern, body_expr: new_expr, }, @@ -257,7 +257,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern, body_expr: apply_task_await( arena, @@ -272,7 +272,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) Err(..) => AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern, body_expr: arena.alloc(Loc::at(body_expr.region, MalformedSuffixed(body_expr))), }, diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index eed1849aa59..c442a9661fd 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -900,7 +900,7 @@ pub fn apply_task_await<'a>( ]), ), )), - comment: &[], + spaces_middle: &[], body_pattern: arena.alloc(Loc::at( loc_pat.region, Pattern::Identifier { ident: new_ident }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index f76362b71b3..cede3581727 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -62,7 +62,7 @@ Defs { @11-15 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @11-15 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index 77ed3f39ae5..2d178b62b1c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -89,7 +89,7 @@ Defs { @31-43 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index 1dabdc8a733..5490643c6b7 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -62,7 +62,7 @@ Defs { ], ), ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @35-36 Identifier { @@ -113,7 +113,7 @@ Defs { @60-69 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @78-79 Identifier { ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index d440b3a5646..dea71fc56c4 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -78,7 +78,7 @@ Defs { ], ), ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @50-53 Identifier { @@ -134,7 +134,7 @@ Defs { @76-83 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @76-83 Identifier { ident: "#!1_stmt", }, @@ -203,7 +203,7 @@ Defs { @92-99 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @92-99 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index f286626575c..f66d73e594f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -97,7 +97,7 @@ Defs { @25-39 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @25-39 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index 73ca3d1cdc2..277c105190f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -114,7 +114,7 @@ Defs { ), ], ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @95-98 Identifier { @@ -194,7 +194,7 @@ Defs { @140-152 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @140-152 Identifier { ident: "#!0_stmt", }, @@ -316,7 +316,7 @@ Defs { @227-239 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @227-239 Identifier { ident: "#!2_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index df6ca81877f..d0bd047ba5f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -62,7 +62,7 @@ Defs { @11-15 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @11-15 Identifier { ident: "#!2_stmt", }, @@ -122,7 +122,7 @@ Defs { @20-24 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @20-24 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 86f180077dc..6c32fdf3ef5 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -62,7 +62,7 @@ Defs { @11-23 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @11-23 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index c550f1e7fdc..7cf35fee8de 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -69,7 +69,7 @@ Defs { @11-16 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @11-16 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index bee3e93226c..0a14281968f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -62,7 +62,7 @@ Defs { @11-57 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index 4036950a3a7..1882fc19430 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -71,7 +71,7 @@ Defs { @19-30 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @19-30 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index 6f1e5be9967..df33a3f1eb8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -69,7 +69,7 @@ Defs { @18-19 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @24-25 Identifier { ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index c4ad133d866..9e5cd8874c6 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -90,7 +90,7 @@ Defs { @54-65 Inferred, ], ), - comment: [], + spaces_middle: [], body_pattern: @54-65 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 2db593b5e6b..cdc0837b873 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -450,13 +450,13 @@ impl<'a> Formattable for ValueDef<'a> { AnnotatedBody { ann_pattern, ann_type, - comment, + spaces_middle, body_pattern, body_expr, } => { fmt_general_def(ann_pattern, buf, indent, ":", &ann_type.value, newlines); - fmt_annotated_body_comment(buf, indent, comment); + fmt_annotated_body_comment(buf, indent, spaces_middle); buf.newline(); fmt_body(buf, &body_pattern.value, &body_expr.value, indent); @@ -585,9 +585,9 @@ pub fn fmt_defs(buf: &mut Buf, defs: &Defs, indent: u16) { pub fn fmt_annotated_body_comment<'a>( buf: &mut Buf, indent: u16, - comment: &'a [roc_parse::ast::CommentOrNewline<'a>], + spaces_middle: &'a [roc_parse::ast::CommentOrNewline<'a>], ) { - let mut comment_iter = comment.iter(); + let mut comment_iter = spaces_middle.iter(); if let Some(comment_first) = comment_iter.next() { match comment_first { roc_parse::ast::CommentOrNewline::Newline => (), diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index f06be7167b1..1b6d3c629bd 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -801,7 +801,7 @@ pub enum ValueDef<'a> { AnnotatedBody { ann_pattern: &'a Loc>, ann_type: &'a Loc>, - comment: &'a [CommentOrNewline<'a>], + spaces_middle: &'a [CommentOrNewline<'a>], body_pattern: &'a Loc>, body_expr: &'a Loc>, }, @@ -1044,7 +1044,7 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> { ValueDef::AnnotatedBody { ann_pattern: _, ann_type: _, - comment: _, + spaces_middle: _, body_pattern: _, body_expr, } => self.push_pending_from_expr(&body_expr.value), @@ -2726,7 +2726,7 @@ impl<'a> Malformed for ValueDef<'a> { ValueDef::AnnotatedBody { ann_pattern, ann_type, - comment: _, + spaces_middle: _, body_pattern, body_expr, } => { diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 435ff7acf87..9d448e884e7 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -3166,7 +3166,7 @@ fn stmts_to_defs<'a>( let value_def = ValueDef::AnnotatedBody { ann_pattern: arena.alloc(ann_pattern), ann_type: arena.alloc(ann_type), - comment: spaces_middle, + spaces_middle, body_pattern: loc_pattern, body_expr: loc_def_expr, }; @@ -3211,7 +3211,7 @@ pub fn join_alias_to_body<'a>( ValueDef::AnnotatedBody { ann_pattern: arena.alloc(loc_ann_pattern), ann_type: arena.alloc(ann_type), - comment: spaces_middle, + spaces_middle, body_pattern, body_expr, } diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index 231eeeb5996..90c6c8c61bd 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -397,13 +397,13 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> { AnnotatedBody { ann_pattern, ann_type, - comment: _, + spaces_middle: _, body_pattern, body_expr, } => AnnotatedBody { ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), ann_type: arena.alloc(ann_type.remove_spaces(arena)), - comment: &[], + spaces_middle: &[], body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), body_expr: arena.alloc(body_expr.remove_spaces(arena)), }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast index 63f4039b00e..35675991c40 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast @@ -38,7 +38,7 @@ Defs( }, ], }, - comment: [ + spaces_middle: [ Newline, ], body_pattern: @28-31 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast index d15d991386b..47e9d395895 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast @@ -40,7 +40,7 @@ Defs( }, ], }, - comment: [ + spaces_middle: [ Newline, ], body_pattern: @29-32 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast index 673d20b6107..f1344e425ca 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast @@ -32,7 +32,7 @@ SpaceAfter( "Foo", [], ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @15-23 RecordDestructure( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast index 90a58360ee4..272ab394d21 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast @@ -42,7 +42,7 @@ SpaceAfter( }, ], }, - comment: [ + spaces_middle: [ Newline, ], body_pattern: @26-34 Apply( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast index 302814a9adf..ef29dc232ab 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast @@ -32,7 +32,7 @@ SpaceAfter( "Foo", [], ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @15-23 Tuple( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast index 45d52a6f435..e8765f3aa9e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast @@ -62,7 +62,7 @@ Defs { }, ], ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @30-34 Identifier { @@ -168,7 +168,7 @@ Defs { ], ext: None, }, - comment: [ + spaces_middle: [ Newline, ], body_pattern: @190-196 Identifier { @@ -250,7 +250,7 @@ Defs { ], ext: None, }, - comment: [ + spaces_middle: [ Newline, ], body_pattern: @326-334 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast index fe3a44b0003..4f48a51e8e2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast @@ -52,7 +52,7 @@ Defs( [], ), ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @45-50 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index a8116791665..344fc26dabe 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -52,7 +52,7 @@ SpaceAfter( ), }, ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @21-22 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index 9d7b1d6a424..31845addf0b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -44,7 +44,7 @@ SpaceAfter( ext: None, }, ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @22-23 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index 16c6da05c19..5ad93fd90f5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -57,7 +57,7 @@ Defs { [], ), ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @43-55 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast index e4ffc41f7cf..90c57acabe3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast @@ -53,7 +53,7 @@ Defs( ext: None, }, ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @28-29 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast index 0c1fdfd0354..7b8717122b2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast @@ -61,7 +61,7 @@ Defs( ), }, ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @30-31 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast index 5aa0513780c..06dbef8c8d4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast @@ -24,7 +24,7 @@ Defs( "Int", [], ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @10-13 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast index 69d9ef27edd..9d08cc483cd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast @@ -38,7 +38,7 @@ Defs( [], ), ), - comment: [ + spaces_middle: [ Newline, ], body_pattern: @25-28 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast index 13d36694401..710d2e8e9b5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast @@ -34,7 +34,7 @@ SpaceAfter( ], ext: None, }, - comment: [ + spaces_middle: [ Newline, ], body_pattern: @21-26 Identifier { From 0e2c42d4391dcd9572f9cb2a85c72e9d51f3c709 Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 29 Jul 2024 10:08:27 -0400 Subject: [PATCH 124/203] update lsp crate --- crates/language_server/src/analysis/tokens.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 22816f51422..fc7a58d9a1c 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -627,7 +627,7 @@ impl IterTokens for ValueDef<'_> { ValueDef::AnnotatedBody { ann_pattern, ann_type, - comment: _, + spaces_middle: _, body_pattern, body_expr, } => (ann_pattern.iter_tokens(arena).into_iter()) From 2529fa07215c44052c99bac8912afd3cc4c0bd92 Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 29 Jul 2024 11:10:16 -0400 Subject: [PATCH 125/203] get rid of extra newlines --- crates/compiler/fmt/src/def.rs | 3 +- crates/compiler/test_syntax/tests/test_fmt.rs | 56 +++++++++++++------ 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index cdc0837b873..9bbb0b27e37 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -604,16 +604,17 @@ pub fn fmt_annotated_body_comment<'a>( } for comment_or_newline in comment_iter { - buf.newline(); match comment_or_newline { roc_parse::ast::CommentOrNewline::Newline => (), roc_parse::ast::CommentOrNewline::DocComment(comment_str) => { + buf.newline(); buf.indent(indent); buf.push_str("# #"); buf.spaces(1); buf.push_str(comment_str.trim()); } roc_parse::ast::CommentOrNewline::LineComment(comment_str) => { + buf.newline(); buf.indent(indent); buf.push_str("#"); buf.spaces(1); diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 7386cb3989f..434b343f98f 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6245,7 +6245,7 @@ mod test_fmt { x : i32 x = 1 x - " + " )); } @@ -6256,7 +6256,7 @@ mod test_fmt { x : i32 # comment x = 1 x - " + " )); } @@ -6269,24 +6269,35 @@ mod test_fmt { # comment 2 x = 1 x - " + " )); } #[test] - fn preserve_annotated_body_comments_with_newlines() { - expr_formats_same(indoc!( - r" - x : i32 + fn preserve_annotated_body_comments_without_newlines() { + expr_formats_to( + indoc!( + r" + x : i32 - # comment + # comment - # comment 2 + # comment 2 - x = 1 - x + x = 1 + x " - )); + ), + indoc!( + r" + x : i32 + # comment + # comment 2 + x = 1 + x + " + ), + ); } #[test] @@ -6297,20 +6308,29 @@ mod test_fmt { # x = 1 x - " + " )); } #[test] - fn preserve_annotated_body_with_newlines() { - expr_formats_same(indoc!( - r" + fn preserve_annotated_body_without_newlines() { + expr_formats_to( + indoc!( + r" x : i32 x = 1 x - " - )); + " + ), + indoc!( + r" + x : i32 + x = 1 + x + " + ), + ); } // this is a parse error atm From ddb58a2cd55bf035bf33b03f7a2723b518a36143 Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 29 Jul 2024 13:06:48 -0400 Subject: [PATCH 126/203] `spaces_middle` -> `lines_between` --- crates/compiler/can/src/def.rs | 2 +- crates/compiler/can/src/desugar.rs | 14 +++++++------- crates/compiler/can/src/suffixed.rs | 2 +- .../test_suffixed__suffixed_tests__basic.snap | 2 +- ...t_suffixed__suffixed_tests__closure_simple.snap | 2 +- ...__suffixed_tests__closure_with_annotations.snap | 4 ++-- ...uffixed__suffixed_tests__closure_with_defs.snap | 6 +++--- ...ixed__suffixed_tests__defs_suffixed_middle.snap | 2 +- .../test_suffixed__suffixed_tests__if_complex.snap | 6 +++--- ...ed__suffixed_tests__last_suffixed_multiple.snap | 4 ++-- ...suffixed__suffixed_tests__multi_defs_stmts.snap | 2 +- ..._suffixed__suffixed_tests__multiple_suffix.snap | 2 +- ...est_suffixed__suffixed_tests__simple_pizza.snap | 2 +- ..._suffixed__suffixed_tests__trailing_binops.snap | 2 +- ..._suffixed__suffixed_tests__type_annotation.snap | 2 +- ...st_suffixed__suffixed_tests__when_branches.snap | 2 +- crates/compiler/fmt/src/def.rs | 8 ++++---- crates/compiler/parse/src/ast.rs | 6 +++--- crates/compiler/parse/src/expr.rs | 4 ++-- crates/compiler/parse/src/remove_spaces.rs | 4 ++-- .../pass/ann_closed_union.expr.result-ast | 2 +- .../snapshots/pass/ann_open_union.expr.result-ast | 2 +- .../annotated_record_destructure.expr.result-ast | 2 +- .../pass/annotated_tag_destructure.expr.result-ast | 2 +- .../annotated_tuple_destructure.expr.result-ast | 2 +- .../pass/expect_defs.moduledefs.result-ast | 6 +++--- .../pass/fn_with_record_arg.expr.result-ast | 2 +- .../function_with_tuple_ext_type.expr.result-ast | 2 +- .../pass/function_with_tuple_type.expr.result-ast | 2 +- .../nested_def_annotation.moduledefs.result-ast | 2 +- .../snapshots/pass/tuple_type.expr.result-ast | 2 +- .../snapshots/pass/tuple_type_ext.expr.result-ast | 2 +- .../pass/type_signature_def.expr.result-ast | 2 +- .../type_signature_function_def.expr.result-ast | 2 +- .../snapshots/pass/where_ident.expr.result-ast | 2 +- crates/language_server/src/analysis/tokens.rs | 2 +- 36 files changed, 57 insertions(+), 57 deletions(-) diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index c0b4de7e297..70d95a72ec7 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2969,7 +2969,7 @@ fn to_pending_value_def<'a>( AnnotatedBody { ann_pattern, ann_type, - spaces_middle: _, + lines_between: _, body_pattern, body_expr, } => { diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index b966f759cc5..196991c3507 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -86,13 +86,13 @@ fn desugar_value_def<'a>( AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between: spaces_middle, body_pattern, body_expr, } => AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between: spaces_middle, body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path), body_expr: desugar_expr(arena, body_expr, src, line_info, module_path), }, @@ -170,7 +170,7 @@ fn desugar_value_def<'a>( ext: None, }, )), - spaces_middle: &[], + lines_between: &[], body_pattern: new_pat, body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path), } @@ -235,7 +235,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between: spaces_middle, body_pattern, body_expr, } => { @@ -244,7 +244,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) Ok(new_expr) => AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between: spaces_middle, body_pattern, body_expr: new_expr, }, @@ -257,7 +257,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between: spaces_middle, body_pattern, body_expr: apply_task_await( arena, @@ -272,7 +272,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) Err(..) => AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between: spaces_middle, body_pattern, body_expr: arena.alloc(Loc::at(body_expr.region, MalformedSuffixed(body_expr))), }, diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index c442a9661fd..e70c3dfc669 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -900,7 +900,7 @@ pub fn apply_task_await<'a>( ]), ), )), - spaces_middle: &[], + lines_between: &[], body_pattern: arena.alloc(Loc::at( loc_pat.region, Pattern::Identifier { ident: new_ident }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index cede3581727..2fb0ca6e0b9 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -62,7 +62,7 @@ Defs { @11-15 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @11-15 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index 2d178b62b1c..2224aa43a68 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -89,7 +89,7 @@ Defs { @31-43 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index 5490643c6b7..9fad69b3c80 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -62,7 +62,7 @@ Defs { ], ), ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @35-36 Identifier { @@ -113,7 +113,7 @@ Defs { @60-69 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @78-79 Identifier { ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index dea71fc56c4..8fb08e9977a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -78,7 +78,7 @@ Defs { ], ), ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @50-53 Identifier { @@ -134,7 +134,7 @@ Defs { @76-83 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @76-83 Identifier { ident: "#!1_stmt", }, @@ -203,7 +203,7 @@ Defs { @92-99 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @92-99 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index f66d73e594f..2af3529f437 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -97,7 +97,7 @@ Defs { @25-39 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @25-39 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index 277c105190f..4d62981e3dc 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -114,7 +114,7 @@ Defs { ), ], ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @95-98 Identifier { @@ -194,7 +194,7 @@ Defs { @140-152 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @140-152 Identifier { ident: "#!0_stmt", }, @@ -316,7 +316,7 @@ Defs { @227-239 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @227-239 Identifier { ident: "#!2_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index d0bd047ba5f..0f1482bdad5 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -62,7 +62,7 @@ Defs { @11-15 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @11-15 Identifier { ident: "#!2_stmt", }, @@ -122,7 +122,7 @@ Defs { @20-24 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @20-24 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 6c32fdf3ef5..683654f0ed4 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -62,7 +62,7 @@ Defs { @11-23 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @11-23 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index 7cf35fee8de..15fd92f24da 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -69,7 +69,7 @@ Defs { @11-16 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @11-16 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 0a14281968f..c2c30519787 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -62,7 +62,7 @@ Defs { @11-57 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index 1882fc19430..c00b9edd32f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -71,7 +71,7 @@ Defs { @19-30 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @19-30 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index df33a3f1eb8..1c9cdd4cc56 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -69,7 +69,7 @@ Defs { @18-19 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @24-25 Identifier { ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index 9e5cd8874c6..58860d209c8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -90,7 +90,7 @@ Defs { @54-65 Inferred, ], ), - spaces_middle: [], + lines_between: [], body_pattern: @54-65 Identifier { ident: "#!1_stmt", }, diff --git a/crates/compiler/fmt/src/def.rs b/crates/compiler/fmt/src/def.rs index 9bbb0b27e37..b3b2abd0c9d 100644 --- a/crates/compiler/fmt/src/def.rs +++ b/crates/compiler/fmt/src/def.rs @@ -450,13 +450,13 @@ impl<'a> Formattable for ValueDef<'a> { AnnotatedBody { ann_pattern, ann_type, - spaces_middle, + lines_between, body_pattern, body_expr, } => { fmt_general_def(ann_pattern, buf, indent, ":", &ann_type.value, newlines); - fmt_annotated_body_comment(buf, indent, spaces_middle); + fmt_annotated_body_comment(buf, indent, lines_between); buf.newline(); fmt_body(buf, &body_pattern.value, &body_expr.value, indent); @@ -585,9 +585,9 @@ pub fn fmt_defs(buf: &mut Buf, defs: &Defs, indent: u16) { pub fn fmt_annotated_body_comment<'a>( buf: &mut Buf, indent: u16, - spaces_middle: &'a [roc_parse::ast::CommentOrNewline<'a>], + lines_between: &'a [roc_parse::ast::CommentOrNewline<'a>], ) { - let mut comment_iter = spaces_middle.iter(); + let mut comment_iter = lines_between.iter(); if let Some(comment_first) = comment_iter.next() { match comment_first { roc_parse::ast::CommentOrNewline::Newline => (), diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 1b6d3c629bd..36899d0d8dd 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -801,7 +801,7 @@ pub enum ValueDef<'a> { AnnotatedBody { ann_pattern: &'a Loc>, ann_type: &'a Loc>, - spaces_middle: &'a [CommentOrNewline<'a>], + lines_between: &'a [CommentOrNewline<'a>], body_pattern: &'a Loc>, body_expr: &'a Loc>, }, @@ -1044,7 +1044,7 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> { ValueDef::AnnotatedBody { ann_pattern: _, ann_type: _, - spaces_middle: _, + lines_between: _, body_pattern: _, body_expr, } => self.push_pending_from_expr(&body_expr.value), @@ -2726,7 +2726,7 @@ impl<'a> Malformed for ValueDef<'a> { ValueDef::AnnotatedBody { ann_pattern, ann_type, - spaces_middle: _, + lines_between: _, body_pattern, body_expr, } => { diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 9d448e884e7..3932bf05c41 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -3166,7 +3166,7 @@ fn stmts_to_defs<'a>( let value_def = ValueDef::AnnotatedBody { ann_pattern: arena.alloc(ann_pattern), ann_type: arena.alloc(ann_type), - spaces_middle, + lines_between: spaces_middle, body_pattern: loc_pattern, body_expr: loc_def_expr, }; @@ -3211,7 +3211,7 @@ pub fn join_alias_to_body<'a>( ValueDef::AnnotatedBody { ann_pattern: arena.alloc(loc_ann_pattern), ann_type: arena.alloc(ann_type), - spaces_middle, + lines_between: spaces_middle, body_pattern, body_expr, } diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index 90c6c8c61bd..522920b90cb 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -397,13 +397,13 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> { AnnotatedBody { ann_pattern, ann_type, - spaces_middle: _, + lines_between: _, body_pattern, body_expr, } => AnnotatedBody { ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), ann_type: arena.alloc(ann_type.remove_spaces(arena)), - spaces_middle: &[], + lines_between: &[], body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), body_expr: arena.alloc(body_expr.remove_spaces(arena)), }, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast index 35675991c40..6fb0eb0f0ae 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast @@ -38,7 +38,7 @@ Defs( }, ], }, - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @28-31 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast index 47e9d395895..fb199d01fe8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast @@ -40,7 +40,7 @@ Defs( }, ], }, - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @29-32 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast index f1344e425ca..f5c03a6ca1e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast @@ -32,7 +32,7 @@ SpaceAfter( "Foo", [], ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @15-23 RecordDestructure( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast index 272ab394d21..e956f634b1a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast @@ -42,7 +42,7 @@ SpaceAfter( }, ], }, - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @26-34 Apply( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast index ef29dc232ab..eba38546fc9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast @@ -32,7 +32,7 @@ SpaceAfter( "Foo", [], ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @15-23 Tuple( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast index e8765f3aa9e..499566f0760 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast @@ -62,7 +62,7 @@ Defs { }, ], ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @30-34 Identifier { @@ -168,7 +168,7 @@ Defs { ], ext: None, }, - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @190-196 Identifier { @@ -250,7 +250,7 @@ Defs { ], ext: None, }, - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @326-334 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast index 4f48a51e8e2..4dcdda59900 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast @@ -52,7 +52,7 @@ Defs( [], ), ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @45-50 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index 344fc26dabe..fd55e434f8b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -52,7 +52,7 @@ SpaceAfter( ), }, ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @21-22 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index 31845addf0b..28da2436d3d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -44,7 +44,7 @@ SpaceAfter( ext: None, }, ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @22-23 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index 5ad93fd90f5..9fb8d977cb1 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -57,7 +57,7 @@ Defs { [], ), ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @43-55 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast index 90c57acabe3..bc2f677b821 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast @@ -53,7 +53,7 @@ Defs( ext: None, }, ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @28-29 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast index 7b8717122b2..521e7017f25 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast @@ -61,7 +61,7 @@ Defs( ), }, ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @30-31 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast index 06dbef8c8d4..1f7255f3578 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast @@ -24,7 +24,7 @@ Defs( "Int", [], ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @10-13 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast index 9d08cc483cd..536c1aeb4da 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast @@ -38,7 +38,7 @@ Defs( [], ), ), - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @25-28 Identifier { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast index 710d2e8e9b5..13c38a37529 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast @@ -34,7 +34,7 @@ SpaceAfter( ], ext: None, }, - spaces_middle: [ + lines_between: [ Newline, ], body_pattern: @21-26 Identifier { diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index fc7a58d9a1c..982d67c612a 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -627,7 +627,7 @@ impl IterTokens for ValueDef<'_> { ValueDef::AnnotatedBody { ann_pattern, ann_type, - spaces_middle: _, + lines_between: _, body_pattern, body_expr, } => (ann_pattern.iter_tokens(arena).into_iter()) From 4bf515461067414e18c91949d0e5e5455165c6bb Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 29 Jul 2024 13:11:57 -0400 Subject: [PATCH 127/203] remove `spaces_middle` from `crates/compiler/can/src/desugar.rs` --- crates/compiler/can/src/desugar.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 196991c3507..cba1b7df4dd 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -86,13 +86,13 @@ fn desugar_value_def<'a>( AnnotatedBody { ann_pattern, ann_type, - lines_between: spaces_middle, + lines_between, body_pattern, body_expr, } => AnnotatedBody { ann_pattern, ann_type, - lines_between: spaces_middle, + lines_between, body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path), body_expr: desugar_expr(arena, body_expr, src, line_info, module_path), }, @@ -235,7 +235,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) AnnotatedBody { ann_pattern, ann_type, - lines_between: spaces_middle, + lines_between, body_pattern, body_expr, } => { @@ -244,7 +244,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) Ok(new_expr) => AnnotatedBody { ann_pattern, ann_type, - lines_between: spaces_middle, + lines_between, body_pattern, body_expr: new_expr, }, @@ -257,7 +257,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) AnnotatedBody { ann_pattern, ann_type, - lines_between: spaces_middle, + lines_between, body_pattern, body_expr: apply_task_await( arena, @@ -272,7 +272,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) Err(..) => AnnotatedBody { ann_pattern, ann_type, - lines_between: spaces_middle, + lines_between, body_pattern, body_expr: arena.alloc(Loc::at(body_expr.region, MalformedSuffixed(body_expr))), }, From 4e8b36adbfb0836cab340ef215eb7bca769b020e Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Mon, 29 Jul 2024 17:37:35 -0700 Subject: [PATCH 128/203] Fix auto-fixing of deprecated interpolated strings --- crates/compiler/parse/src/remove_spaces.rs | 2 +- ...ted_interpolated_string.expr.formatted.roc | 1 + ...ecated_interpolated_string.expr.result-ast | 17 ++ .../deprecated_interpolated_string.expr.roc | 1 + .../pass/pizza_bang.moduledefs.formatted.roc | 8 + .../pass/pizza_bang.moduledefs.result-ast | 168 ++++++++++++++++++ .../snapshots/pass/pizza_bang.moduledefs.roc | 8 + .../test_syntax/tests/test_snapshots.rs | 2 + 8 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index 96705783232..ff206e3e1df 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -593,7 +593,7 @@ impl<'a> RemoveSpaces<'a> for StrSegment<'a> { StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c), StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)), StrSegment::DeprecatedInterpolated(t) => { - StrSegment::DeprecatedInterpolated(t.remove_spaces(arena)) + StrSegment::Interpolated(t.remove_spaces(arena)) } } } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.formatted.roc new file mode 100644 index 00000000000..0ff8b60204f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.formatted.roc @@ -0,0 +1 @@ +"$(e)" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.result-ast new file mode 100644 index 00000000000..31432b47f4a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.result-ast @@ -0,0 +1,17 @@ +SpaceAfter( + Str( + Line( + [ + DeprecatedInterpolated( + @3-4 Var { + module_name: "", + ident: "e", + }, + ), + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.roc new file mode 100644 index 00000000000..b48439b918d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/deprecated_interpolated_string.expr.roc @@ -0,0 +1 @@ +"\(e)" diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc new file mode 100644 index 00000000000..2c5a82c0270 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.formatted.roc @@ -0,0 +1,8 @@ +main = + Arg.list! {} + |> List.dropFirst 1 + |> List.mapTry Str.toU8 + |> Task.fromResult! + |> List.sum + |> \total -> "Sum of numbers: $(Num.toStr total)" + |> Stdout.line! diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast new file mode 100644 index 00000000000..45b2889f076 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast @@ -0,0 +1,168 @@ +Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-189, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-4 Identifier { + ident: "main", + }, + @11-189 SpaceBefore( + BinOps( + [ + ( + @11-23 SpaceAfter( + Apply( + @11-19 TaskAwaitBang( + Var { + module_name: "Arg", + ident: "list", + }, + ), + [ + @21-23 Record( + [], + ), + ], + Space, + ), + [ + Newline, + ], + ), + @28-30 Pizza, + ), + ( + @31-47 SpaceAfter( + Apply( + @31-45 Var { + module_name: "List", + ident: "dropFirst", + }, + [ + @46-47 Num( + "1", + ), + ], + Space, + ), + [ + Newline, + ], + ), + @52-54 Pizza, + ), + ( + @55-75 SpaceAfter( + Apply( + @55-66 Var { + module_name: "List", + ident: "mapTry", + }, + [ + @67-75 Var { + module_name: "Str", + ident: "toU8", + }, + ], + Space, + ), + [ + Newline, + ], + ), + @80-82 Pizza, + ), + ( + @83-98 SpaceAfter( + TaskAwaitBang( + Var { + module_name: "Task", + ident: "fromResult", + }, + ), + [ + Newline, + ], + ), + @104-106 Pizza, + ), + ( + @107-115 SpaceAfter( + Var { + module_name: "List", + ident: "sum", + }, + [ + Newline, + ], + ), + @120-122 Pizza, + ), + ( + @123-169 SpaceAfter( + Closure( + [ + @124-129 Identifier { + ident: "total", + }, + ], + @133-169 Str( + Line( + [ + Plaintext( + "Sum of numbers: ", + ), + Interpolated( + @152-167 Apply( + @152-161 Var { + module_name: "Num", + ident: "toStr", + }, + [ + @162-167 Var { + module_name: "", + ident: "total", + }, + ], + Space, + ), + ), + ], + ), + ), + ), + [ + Newline, + ], + ), + @174-176 Pizza, + ), + ], + @177-188 TaskAwaitBang( + Var { + module_name: "Stdout", + ident: "line", + }, + ), + ), + [ + Newline, + ], + ), + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc new file mode 100644 index 00000000000..177bf92dfab --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.roc @@ -0,0 +1,8 @@ +main = + Arg.list! {} + |> List.dropFirst 1 + |> List.mapTry Str.toU8 + |> Task.fromResult! + |> List.sum + |> \total -> "Sum of numbers: $(Num.toStr total)" + |> Stdout.line! diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index c4ceb8de79e..27736ae7a61 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -304,6 +304,7 @@ mod test_snapshots { pass/dbg.expr, pass/dbg_multiline.expr, pass/defs_suffixed_middle_extra_indents.moduledefs, + pass/deprecated_interpolated_string.expr, pass/destructure_tag_assignment.expr, pass/docs.expr, pass/empty_app_header.header, @@ -438,6 +439,7 @@ mod test_snapshots { pass/pattern_as_list_rest.expr, pass/pattern_as_spaces.expr, pass/pattern_with_space_in_parens.expr, // https://github.com/roc-lang/roc/issues/929 + pass/pizza_bang.moduledefs, pass/plus_if.expr, pass/plus_when.expr, pass/pos_inf_float.expr, From 939f9cb7e9895ddc246a48f8c70cd93d1a0e7b76 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Mon, 29 Jul 2024 22:54:25 -0700 Subject: [PATCH 129/203] Give an error for record builders in patterns instead of crashing --- crates/compiler/parse/src/expr.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 8b7948994d1..f5f1aaa04a5 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -2113,11 +2113,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result unreachable!(), + Expr::SpaceBefore(..) | Expr::SpaceAfter(..) | Expr::ParensAround(..) => unreachable!(), Expr::Record(fields) => { let patterns = fields.map_items_result(arena, |loc_assigned_field| { @@ -2172,7 +2168,9 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result return Err(()), + | Expr::Crash + | Expr::OldRecordBuilder(..) + | Expr::RecordBuilder { .. } => return Err(()), Expr::Str(string) => Pattern::StrLiteral(string), Expr::SingleQuote(string) => Pattern::SingleQuote(string), From f0c4415eab11b0b2330e1aa5b1594dca7751c93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Geron?= Date: Tue, 30 Jul 2024 21:37:25 +1200 Subject: [PATCH 130/203] Add concatMap and equivalent of zip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit joinMap <=> concatMap List.map2 x y Pair <=> zip Signed-off-by: Aurélien Geron --- www/public/different-names/index.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/www/public/different-names/index.html b/www/public/different-names/index.html index 66b81740d3f..e2f89765939 100644 --- a/www/public/different-names/index.html +++ b/www/public/different-names/index.html @@ -112,6 +112,7 @@

Different Names

List.joinMap
    +
  • concatMap
  • filterMap
  • filter_map
@@ -137,6 +138,14 @@

Different Names

+ + List.map2 x y Pair + +
    +
  • zip
  • +
+ + From 30a76a092bc384affae6ed2f5e55af2f492c499d Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:06:23 +0200 Subject: [PATCH 131/203] improve opaque types tip --- crates/cli/tests/cli_run.rs | 6 ++---- crates/compiler/load/tests/test_reporting.rs | 18 ++++++------------ crates/reporting/src/error/type.rs | 19 ++++++++----------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 81a5ffcc2c2..0880464e05b 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -1509,10 +1509,8 @@ mod cli_run { Effect.Effect (Result {} []) - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. + Tip: Add type annotations to functions or values to help you figure + this out. ──────────────────────────────────────────────────────────────────────────────── diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 71418e4288c..82a493634cb 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -8240,10 +8240,8 @@ In roc, functions are always written as a lambda, like{} F Str - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. + Tip: *Add type annotations* to functions or values to help you figure + this out. "# ); @@ -8277,10 +8275,8 @@ In roc, functions are always written as a lambda, like{} Age - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. + Tip: *Add type annotations* to functions or values to help you figure + this out. " ); @@ -10403,10 +10399,8 @@ In roc, functions are always written as a lambda, like{} OList - Tip: Type comparisons between an opaque type are only ever equal if - both types are the same opaque type. Did you mean to create an opaque - type by wrapping it? If I have an opaque type Age := U32 I can create - an instance of this opaque type by doing @Age 23. + Tip: *Add type annotations* to functions or values to help you figure + this out. " ); diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index b13c24781cc..9673a701688 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -4761,17 +4761,14 @@ fn type_problem_to_pretty<'b>( alloc.reflow("Learn more about optional fields at TODO."), ])), - (OpaqueComparedToNonOpaque, _) => alloc.tip().append(alloc.concat([ - alloc.reflow( - "Type comparisons between an opaque type are only ever \ - equal if both types are the same opaque type. Did you mean \ - to create an opaque type by wrapping it? If I have an opaque type ", - ), - alloc.type_str("Age := U32"), - alloc.reflow(" I can create an instance of this opaque type by doing "), - alloc.type_str("@Age 23"), - alloc.reflow("."), - ])), + (OpaqueComparedToNonOpaque, _) => alloc.tip().append( + alloc.concat([ + alloc + .reflow("Add type annotations") + .annotate(Annotation::Emphasized), + alloc.reflow(" to functions or values to help you figure this out."), + ]), + ), (BoolVsBoolTag(tag), _) => alloc.tip().append(alloc.concat([ alloc.reflow("Did you mean to use "), From 3136c0b7470e2b24870cda2e5fef30bb09c82ed7 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 30 Jul 2024 19:58:01 +0200 Subject: [PATCH 132/203] expose roc debug cli to nix --- flake.nix | 3 + nix/builder.nix | 162 +++++++++++++++++++++++---------------------- nix/default.nix | 13 ++-- nix/fileFilter.nix | 2 + 4 files changed, 98 insertions(+), 82 deletions(-) diff --git a/flake.nix b/flake.nix index b8e952c3f95..8487f2bd822 100644 --- a/flake.nix +++ b/flake.nix @@ -150,7 +150,10 @@ full = rocBuild.roc-full; # only the CLI crate = executable provided in nightly releases cli = rocBuild.roc-cli; + cli-debug = rocBuild.roc-cli-debug; + lang-server = rocBuild.roc-lang-server; + lang-server-debug = rocBuild.roc-lang-server-debug; }; apps = { diff --git a/nix/builder.nix b/nix/builder.nix index 31fc1df6821..48782e54c5a 100644 --- a/nix/builder.nix +++ b/nix/builder.nix @@ -5,92 +5,98 @@ let subPackagePath = if subPackage != null then "crates/${subPackage}" else null; mainBin = if subPackage == "language_server" then "roc_language_server" else "roc"; filteredSource = pkgs.callPackage ./fileFilter.nix { }; -in -rustPlatform.buildRustPackage { - pname = "roc" + lib.optionalString (subPackage != null) "_${subPackage}"; - version = "0.0.1"; + buildRoc = buildTypeStr: + rustPlatform.buildRustPackage { + pname = "roc" + lib.optionalString (subPackage != null) "_${subPackage}"; + version = "0.0.1"; + buildType = buildTypeStr; - buildAndTestSubdir = subPackagePath; + buildAndTestSubdir = subPackagePath; - src = filteredSource; + src = filteredSource; - cargoLock = { - lockFile = ../Cargo.lock; - outputHashes = { - "criterion-0.3.5" = "sha256-+FibPQGiR45g28xCHcM0pMN+C+Q8gO8206Wb5fiTy+k="; - "inkwell-0.2.0" = "sha256-VhTapYGonoSQ4hnDoLl4AAgj0BppAhPNA+UPuAJSuAU="; - "plotters-0.3.1" = "sha256-noy/RSjoEPZZbOJTZw1yxGcX5S+2q/7mxnUrzDyxOFw="; - "rustyline-9.1.1" = "sha256-aqQqz6nSp+Qn44gm3jXmmQUO6/fYTx7iLph2tbA24Bs="; - }; - }; + cargoLock = { + lockFile = ../Cargo.lock; + outputHashes = { + "criterion-0.3.5" = "sha256-+FibPQGiR45g28xCHcM0pMN+C+Q8gO8206Wb5fiTy+k="; + "inkwell-0.2.0" = "sha256-VhTapYGonoSQ4hnDoLl4AAgj0BppAhPNA+UPuAJSuAU="; + "plotters-0.3.1" = "sha256-noy/RSjoEPZZbOJTZw1yxGcX5S+2q/7mxnUrzDyxOFw="; + "rustyline-9.1.1" = "sha256-aqQqz6nSp+Qn44gm3jXmmQUO6/fYTx7iLph2tbA24Bs="; + }; + }; - shellHook = '' - export LLVM_SYS_${llvmMajorMinorStr}_PREFIX="${llvmPkgs.llvm.dev}" - ''; + shellHook = '' + export LLVM_SYS_${llvmMajorMinorStr}_PREFIX="${llvmPkgs.llvm.dev}" + ''; - # required for zig - XDG_CACHE_HOME = - "xdg_cache"; # prevents zig AccessDenied error github.com/ziglang/zig/issues/6810 - # want to see backtrace in case of failure - RUST_BACKTRACE = 1; + # required for zig + XDG_CACHE_HOME = + "xdg_cache"; # prevents zig AccessDenied error github.com/ziglang/zig/issues/6810 + # want to see backtrace in case of failure + RUST_BACKTRACE = 1; - # skip running rust tests, problems: - # building of example platforms requires network: Could not resolve host - # zig AccessDenied error github.com/ziglang/zig/issues/6810 - # Once instance has previously been poisoned ?? - doCheck = false; + # skip running rust tests, problems: + # building of example platforms requires network: Could not resolve host + # zig AccessDenied error github.com/ziglang/zig/issues/6810 + # Once instance has previously been poisoned ?? + doCheck = false; - nativeBuildInputs = (with pkgs; [ - cmake - git - pkg-config - python3 - llvmPkgs.clang - llvmPkgs.llvm.dev - llvmPkgs.bintools-unwrapped # contains lld - zigPkg - ]); + nativeBuildInputs = (with pkgs; [ + cmake + git + pkg-config + python3 + llvmPkgs.clang + llvmPkgs.llvm.dev + llvmPkgs.bintools-unwrapped # contains lld + zigPkg + ]); - buildInputs = (with pkgs; - [ - libffi - libxml2 - ncurses - zlib - cargo - makeWrapper # necessary for postBuild wrapProgram - ] ++ lib.optionals pkgs.stdenv.isDarwin - (with pkgs.darwin.apple_sdk.frameworks; - [ - AppKit - CoreFoundation - CoreServices - Foundation - Security - ])); + buildInputs = (with pkgs; + [ + libffi + libxml2 + ncurses + zlib + cargo + makeWrapper # necessary for postBuild wrapProgram + ] ++ lib.optionals pkgs.stdenv.isDarwin + (with pkgs.darwin.apple_sdk.frameworks; + [ + AppKit + CoreFoundation + CoreServices + Foundation + Security + ])); - # cp: to copy str.zig,list.zig... - # wrapProgram pkgs.stdenv.cc: to make ld available for compiler/build/src/link.rs - postInstall = - let - binPath = - lib.makeBinPath [ pkgs.stdenv.cc ]; - linuxArgs = lib.optionalString pkgs.stdenv.isLinux - "--set NIX_GLIBC_PATH ${glibcPath} --set NIX_LIBGCC_S_PATH ${libGccSPath}"; - rocPath = "$out/bin/roc"; - wrapRoc = "wrapProgram ${rocPath} ${linuxArgs} --prefix PATH : ${binPath}"; - in - # need to check if roc bin exists since it might not if subPackage is set - '' - if test -f ${rocPath}; then - ${wrapRoc} - fi - ''; + # cp: to copy str.zig,list.zig... + # wrapProgram pkgs.stdenv.cc: to make ld available for compiler/build/src/link.rs + postInstall = + let + binPath = + lib.makeBinPath [ pkgs.stdenv.cc ]; + linuxArgs = lib.optionalString pkgs.stdenv.isLinux + "--set NIX_GLIBC_PATH ${glibcPath} --set NIX_LIBGCC_S_PATH ${libGccSPath}"; + rocPath = "$out/bin/roc"; + wrapRoc = "wrapProgram ${rocPath} ${linuxArgs} --prefix PATH : ${binPath}"; + in + # need to check if roc bin exists since it might not if subPackage is set + '' + if test -f ${rocPath}; then + ${wrapRoc} + fi + ''; - # https://ryantm.github.io/nixpkgs/stdenv/meta/ - meta = { - homepage = "https://www.roc-lang.org/"; - license = lib.licenses.upl; - mainProgram = mainBin; - }; + # https://ryantm.github.io/nixpkgs/stdenv/meta/ + meta = { + homepage = "https://www.roc-lang.org/"; + license = lib.licenses.upl; + mainProgram = mainBin; + }; + }; +in +{ + roc-debug = buildRoc "debug"; + roc-release = buildRoc "release"; } diff --git a/nix/default.nix b/nix/default.nix index 8a9e167816b..e54aa8a2acf 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -16,11 +16,16 @@ let rust-shell = (rustVersion.override { extensions = [ "rust-src" "rust-analyzer" ]; }); - # all rust crates in workspace.members of Cargo.toml - roc-full = callPackage ./builder.nix { }; - roc-lang-server = callPackage ./builder.nix { subPackage = "language_server"; }; + # contains all rust crates in workspace.members of Cargo.toml + roc-full = (callPackage ./builder.nix { }).roc-release; + roc-full-debug = (callPackage ./builder.nix { }).roc-debug; + + roc-lang-server = (callPackage ./builder.nix { subPackage = "language_server"; }).roc-release; + roc-lang-server-debug = (callPackage ./builder.nix { subPackage = "language_server"; }).roc-debug; + # only the CLI crate = executable provided in nightly releases - roc-cli = callPackage ./builder.nix { subPackage = "cli"; }; + roc-cli = (callPackage ./builder.nix { subPackage = "cli"; }).roc-release; + roc-cli-debug = (callPackage ./builder.nix { subPackage = "cli"; }).roc-debug; }; in diff --git a/nix/fileFilter.nix b/nix/fileFilter.nix index 8f974f85b21..a8c0fb7cab5 100644 --- a/nix/fileFilter.nix +++ b/nix/fileFilter.nix @@ -37,6 +37,8 @@ let ../Cargo.toml ../Cargo.lock ../version.txt + ../rust-toolchain.toml + ../.cargo/config.toml onlyCratesFolder ]; From 0b9c6ff7d8a11d4aa4b065bd32d0c26c6e5294d3 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 30 Jul 2024 20:07:54 +0200 Subject: [PATCH 133/203] update devtools flake --- devtools/flake.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/devtools/flake.lock b/devtools/flake.lock index 4d880925a3c..11b30cd5d98 100644 --- a/devtools/flake.lock +++ b/devtools/flake.lock @@ -101,8 +101,8 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1712750088, - "narHash": "sha256-qao1J0xx+eCuCdOTCf4wPkkKmwHdJy1uof4QKhO/yOc=", + "lastModified": 1722362288, + "narHash": "sha256-tjwYO8AH2Skwhynm/fwOTodN/EqLgAbvKzhE/xxQ2r8=", "path": "/home/username/gitrepos/roc", "type": "path" }, @@ -129,11 +129,11 @@ ] }, "locked": { - "lastModified": 1712369449, - "narHash": "sha256-tbWug3uXPlSm1j0xD80Y3xbP+otT6gLnQo1e/vQat48=", + "lastModified": 1713150335, + "narHash": "sha256-Ic7zCPfiSYc9nFFp+E44WFk3TBJ99J/uPZ4QXX+uPPw=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "41b3b080cc3e4b3a48e933b87fc15a05f1870779", + "rev": "b186d85e747e2b7bee220ec95839fb66c868dc47", "type": "github" }, "original": { From d55ff26ae2f036812f28e0a60bddacb69e776c93 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 30 Jul 2024 20:10:18 +0200 Subject: [PATCH 134/203] add debug tip Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5449362132..0b124012a8e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ Execute `cargo fmt --all` to fix the formatting.
:beetle: Debugging Tips - +- Use a debug build of the compiler. We have many asserts enabled in the debug compiler that can alert you to something going wrong. When building from source, build the debug compiler with `cargo build --bin roc`, the binary is at roc/target/debug/roc. When using roc through a nix flake like in [basic-cli](https://github.com/roc-lang/basic-cli), use `rocPkgs.cli-debug` instead of `rocPkgs.cli`. - At the bottom of [.cargo/config.toml](https://github.com/roc-lang/roc/blob/main/.cargo/config.toml) we have useful debug flags that activate certain debug prints. - For Roc code; minimize the code that produces the issue. - For segmentation faults: From 1a8a739250bbaa8bed16db22acc2c6f06b4db041 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:45:56 +0200 Subject: [PATCH 135/203] cargo test in debug mode Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- .github/workflows/ci_manager.yml | 6 ++++ .github/workflows/ubuntu_x86_64_debug.yml | 38 +++++++++++++++++++++++ Cargo.lock | 1 + crates/cli_utils/Cargo.toml | 1 + crates/cli_utils/src/helpers.rs | 23 ++++++++------ 5 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/ubuntu_x86_64_debug.yml diff --git a/.github/workflows/ci_manager.yml b/.github/workflows/ci_manager.yml index a33b81920dc..8d18f8dc996 100644 --- a/.github/workflows/ci_manager.yml +++ b/.github/workflows/ci_manager.yml @@ -104,6 +104,11 @@ jobs: if: needs.check-changes.outputs.run_tests == 'full' uses: ./.github/workflows/ubuntu_x86_64.yml + start-ubuntu-x86-64-tests-debug: + needs: check-changes + if: needs.check-changes.outputs.run_tests == 'full' + uses: ./.github/workflows/ubuntu_x86_64_debug.yml + start-windows-release-build-test: needs: check-changes if: needs.check-changes.outputs.run_tests == 'full' @@ -128,6 +133,7 @@ jobs: start-nix-macos-apple-silicon-tests, start-macos-x86-64-tests, start-ubuntu-x86-64-tests, + start-ubuntu-x86-64-tests-debug, start-windows-release-build-test, start-windows-tests, start-roc-benchmarks diff --git a/.github/workflows/ubuntu_x86_64_debug.yml b/.github/workflows/ubuntu_x86_64_debug.yml new file mode 100644 index 00000000000..2ad30f07522 --- /dev/null +++ b/.github/workflows/ubuntu_x86_64_debug.yml @@ -0,0 +1,38 @@ +on: + workflow_call: + +name: cargo test debug + +env: + RUST_BACKTRACE: 1 + +jobs: + cargo-test-debug: + name: cargo test debug + runs-on: [ubuntu-22.04] + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + - uses: goto-bus-stop/setup-zig@v2 + with: + version: 0.11.0 + + - run: zig version + + - name: Install dependencies + run: | + sudo apt -y install build-essential libunwind-dev pkg-config zlib1g-dev valgrind + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 16 + sudo rm /usr/bin/clang + sudo ln -s /usr/bin/clang-16 /usr/bin/clang + sudo ln -s /usr/bin/lld-16 /usr/bin/ld.lld + sudo apt -y install libpolly-16-dev + + # for skipped tests; see #6946, #6947 + - name: cargo test without --release + env: + RUSTFLAGS: -C link-arg=-fuse-ld=lld + run: cargo test -- --skip tests/exhaustive/match_on_result_with_uninhabited_error_destructuring_in_lambda_syntax.txt --skip tests::identity_lambda --skip tests::issue_2300 --skip tests::issue_2582_specialize_result_value --skip tests::sum_lambda diff --git a/Cargo.lock b/Cargo.lock index e44c1472b98..051e2aecb85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -468,6 +468,7 @@ version = "0.0.1" dependencies = [ "bumpalo", "criterion", + "regex", "rlimit", "roc_collections", "roc_command_utils", diff --git a/crates/cli_utils/Cargo.toml b/crates/cli_utils/Cargo.toml index 2530866df57..b20e9d10d96 100644 --- a/crates/cli_utils/Cargo.toml +++ b/crates/cli_utils/Cargo.toml @@ -17,6 +17,7 @@ roc_command_utils = { path = "../utils/command" } bumpalo.workspace = true criterion.workspace = true +regex.workspace = true serde-xml-rs.workspace = true serde.workspace = true tempfile.workspace = true diff --git a/crates/cli_utils/src/helpers.rs b/crates/cli_utils/src/helpers.rs index 6a46b2479e9..ad445ec06ac 100644 --- a/crates/cli_utils/src/helpers.rs +++ b/crates/cli_utils/src/helpers.rs @@ -4,6 +4,7 @@ extern crate roc_load; extern crate roc_module; extern crate tempfile; +use regex::Regex; use roc_command_utils::{cargo, pretty_command_string, root_dir}; use serde::Deserialize; use serde_xml_rs::from_str; @@ -49,19 +50,23 @@ where } pub fn has_error(stderr: &str) -> bool { - let stderr_stripped = stderr - .replacen("🔨 Rebuilding platform...\n", "", 1) - // for some reason, llvm prints out this warning when targeting windows - .replacen( - "warning: ignoring debug info with an invalid version (0) in app\r\n", - "", - 1, - ); + let stderr_stripped = { + let regx = Regex::new(r"\[.*? / .*?\]").unwrap(); // e.g. [20.7 / 20.7 MB] when downloading platform + regx.replace_all(stderr, "") + } + .replace('\u{1b}', "") // also shown when downloading platform + .replacen("🔨 Rebuilding platform...\n", "", 1) + // for some reason, llvm prints out this warning when targeting windows + .replacen( + "warning: ignoring debug info with an invalid version (0) in app\r\n", + "", + 1, + ); let is_reporting_runtime = stderr_stripped.starts_with("runtime: ") && stderr_stripped.ends_with("ms\n"); - let is_clean = stderr_stripped.is_empty() || + let is_clean = stderr_stripped.trim().is_empty() || is_reporting_runtime || // macOS ld reports this warning, but if we remove -undefined dynamic_lookup, // linking stops working properly. From 25fd477f64b01e348603afe5b3ff03916386b9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Geron?= Date: Thu, 1 Aug 2024 08:19:44 +1200 Subject: [PATCH 136/203] Remove "backpassing" and add "the `!` operator" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This interactive code example does not include backpassing, unless I'm mistaken. Instead, it uses the `!` operator. I also reordered the syntax sugar list to match the order they appear in the example. Signed-off-by: Aurélien Geron --- www/content/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/content/index.md b/www/content/index.md index c410346cc1f..79a5d6e129a 100644 --- a/www/content/index.md +++ b/www/content/index.md @@ -110,7 +110,7 @@ Here's a code sample that shows a few different aspects of Roc: - File I/O and HTTP requests - Pattern matching for error handling - JSON deserialization via type inference -- Common syntax sugar: string interpolation, pipelines, and backpassing +- Common syntax sugar: pipelines, the `!` operator, and string interpolation The [tutorial](/tutorial) introduces these gradually and in more depth, but this gives a brief overview. From b6d3bb63d32c6670f6469102579b88997f285cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Geron?= Date: Thu, 1 Aug 2024 13:04:26 +1200 Subject: [PATCH 137/203] Improve Result-chaining documentation --- roc-for-elm-programmers.md | 2 ++ www/content/tutorial.md | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/roc-for-elm-programmers.md b/roc-for-elm-programmers.md index 557c74b6456..39242d9a5ad 100644 --- a/roc-for-elm-programmers.md +++ b/roc-for-elm-programmers.md @@ -931,6 +931,8 @@ foo 1 2 if something then 3 else 4 ## Backpassing +Note: the backpassing syntax will likely be replaced with a new `?` operator in the future, see [this discussion](https://github.com/roc-lang/roc/issues/6828) for more details. + Suppose I'm using a platform for making a CLI, and I want to run several `Task`s in a row which read some files from the disk. Here's one way I could do that, assuming `Task.await` is like Elm's `Task.andThen` with arguments flipped: diff --git a/www/content/tutorial.md b/www/content/tutorial.md index 11eb1dcf899..c4ca87d5147 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -819,6 +819,20 @@ Result.isOk (List.get ["a", "b", "c"] 1) # Note: There's a Result.isErr function that works similarly. ``` +```roc +Result.try (Str.toU64 "2") \idx -> List.get ["a", "b", "c", "d"] idx +# returns `Ok "c"` + +# Notes: +# - `Str.toU64 "2"` parses the string "2" to the integer 2, and returns `Ok 2` (more on +# integer types later) +# - since parsing is successful, `Result.try` passes 2 to the function `\idx -> ...` +# - passing "abc" or "1000" instead of "2" would have resulted in `Err InvalidNumStr` +# or `Err OutOfBounds` respectively +``` + +`Result.try` is often used to chain functions that can fail (as in the example above), returning the first error if any occurs. There is [a discussion](https://github.com/roc-lang/roc/issues/6828) to add syntax sugar for such chaining. + ### [Walking the elements in a list](#walking-the-elements-in-a-list) {#walking-the-elements-in-a-list} We've now seen a few different ways you can transform lists. Sometimes, though, there's nothing From 217f62f6beb4df486c7df61be2d41e00003df3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Geron?= Date: Fri, 2 Aug 2024 20:44:02 +1200 Subject: [PATCH 138/203] Update and clarify reserved vs non-reserved keywords in the tutorial --- www/content/tutorial.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index c4ca87d5147..e507a130f1d 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -2246,9 +2246,13 @@ See the [Record Builder Example](https://www.roc-lang.org/examples/RecordBuilder ### [Reserved Keywords](#reserved-keywords) {#reserved-keywords} -These are all the reserved keywords in Roc. You can't choose any of these as names, except as record field names. +These are reserved keywords in Roc. You can't choose any of them as names, except as record field names. -`if`, `then`, `else`, `when`, `as`, `is`, `dbg`, `import`, `expect`, `expect-fx`, `crash`, `module`, `app`, `package`, `platform`, `hosted`, `exposes`, `with`, `generates`, `packages`, `requires` +`as`, `crash`, `dbg`, `else`, `expect`, `expect-fx`, `if`, `import`, `is`, `then`, `when` + +Other keywords are used in unambiguous contexts, so they are not reserved. This includes: + +`app`, `exposes`, `exposing`, `generates`, `implements`, `module`, `package`, `packages`, `platform`, `requires`, `where`, `with` ## [Operator Desugaring Table](#operator-desugaring-table) {#operator-desugaring-table} From 7d2d62519adf34fdbf66c5f9fd238e113d54b9fb Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:40:26 +0200 Subject: [PATCH 139/203] tutorial improvements Some follow-up on #6952. --- roc-for-elm-programmers.md | 2 +- www/content/tutorial.md | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/roc-for-elm-programmers.md b/roc-for-elm-programmers.md index 39242d9a5ad..86dc1c67029 100644 --- a/roc-for-elm-programmers.md +++ b/roc-for-elm-programmers.md @@ -931,7 +931,7 @@ foo 1 2 if something then 3 else 4 ## Backpassing -Note: the backpassing syntax will likely be replaced with a new `?` operator in the future, see [this discussion](https://github.com/roc-lang/roc/issues/6828) for more details. +Note: the backpassing syntax will likely be removed from the language. The [`!` suffix](https://www.roc-lang.org/tutorial#the-!-suffix) can instead be used for `Task.await` and the `?` suffix is [planned](https://github.com/roc-lang/roc/issues/6828) for use with `Result.try`. Suppose I'm using a platform for making a CLI, and I want to run several `Task`s in a row which read some files from the disk. Here's one way I could do diff --git a/www/content/tutorial.md b/www/content/tutorial.md index c4ca87d5147..dfec9f12b18 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -820,18 +820,29 @@ Result.isOk (List.get ["a", "b", "c"] 1) ``` ```roc -Result.try (Str.toU64 "2") \idx -> List.get ["a", "b", "c", "d"] idx -# returns `Ok "c"` +Result.try (Str.toU64 "2") listGet + +listGet = U64 -> Result Str [OutOfBounds] +listGet = \index -> + List.get ["a", "b", "c", "d"] index + +# Running returns `Ok "c"` # Notes: # - `Str.toU64 "2"` parses the string "2" to the integer 2, and returns `Ok 2` (more on # integer types later) -# - since parsing is successful, `Result.try` passes 2 to the function `\idx -> ...` +# - since parsing is successful, `Result.try` passes 2 to the `listGet`` function # - passing "abc" or "1000" instead of "2" would have resulted in `Err InvalidNumStr` # or `Err OutOfBounds` respectively ``` -`Result.try` is often used to chain functions that can fail (as in the example above), returning the first error if any occurs. There is [a discussion](https://github.com/roc-lang/roc/issues/6828) to add syntax sugar for such chaining. +`Result.try` is often used to chain two functions that return `Result` (as in the example above). This prevents you from needing to add error handling code at every intermediate step. + +
+ Upcoming feature + + We plan to introduce syntax sugar to make `Result.try` nicer to use, follow [this issue](https://github.com/roc-lang/roc/issues/6828) for more. +
### [Walking the elements in a list](#walking-the-elements-in-a-list) {#walking-the-elements-in-a-list} @@ -1596,7 +1607,7 @@ main = Stdout.line! "Hello, World!" ``` -The `Stdout.line` function takes a `Str` and writes it to [standard output](). (We'll discuss the `!` part later.) `Stdout.line` has this type: +The `Stdout.line` function takes a `Str` and writes it to [standard output](), we'll [discuss the `!` part later](https://www.roc-lang.org/tutorial#the-!-suffix). `Stdout.line` has this type: ```roc Stdout.line : Str -> Task {} * From bb2bae4b8ebd7bd6d1db61b7b2666fb312275b16 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 2 Aug 2024 18:44:02 +0200 Subject: [PATCH 140/203] correct backtick --- www/content/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index dfec9f12b18..81fa684e9aa 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -831,7 +831,7 @@ listGet = \index -> # Notes: # - `Str.toU64 "2"` parses the string "2" to the integer 2, and returns `Ok 2` (more on # integer types later) -# - since parsing is successful, `Result.try` passes 2 to the `listGet`` function +# - since parsing is successful, `Result.try` passes 2 to the `listGet` function # - passing "abc" or "1000" instead of "2" would have resulted in `Err InvalidNumStr` # or `Err OutOfBounds` respectively ``` From 96e643fc339f618960c9312f7a0e5b78199d1dff Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 2 Aug 2024 20:03:36 +0200 Subject: [PATCH 141/203] simplify language Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- www/content/tutorial.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index e507a130f1d..2fdd55e0f72 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -2250,7 +2250,7 @@ These are reserved keywords in Roc. You can't choose any of them as names, excep `as`, `crash`, `dbg`, `else`, `expect`, `expect-fx`, `if`, `import`, `is`, `then`, `when` -Other keywords are used in unambiguous contexts, so they are not reserved. This includes: +Other keywords are used only in specific places, so they are not reserved. This includes: `app`, `exposes`, `exposing`, `generates`, `implements`, `module`, `package`, `packages`, `platform`, `requires`, `where`, `with` From 045fe4ff5d5ff1dd69a7d93b8c31c06ac604b0cb Mon Sep 17 00:00:00 2001 From: Lubsch <33580245+Lubsch@users.noreply.github.com> Date: Fri, 2 Aug 2024 23:36:34 +0200 Subject: [PATCH 142/203] Remove duplicated mentioning of glue changes Signed-off-by: Lubsch <33580245+Lubsch@users.noreply.github.com> --- www/content/plans.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/www/content/plans.md b/www/content/plans.md index ac8a5277950..54d62474b3b 100644 --- a/www/content/plans.md +++ b/www/content/plans.md @@ -16,14 +16,6 @@ As an example, we had [a discussion](https://roc.zulipchat.com/#narrow/stream/30 This has been consistently happening a few times per year. It's hard to predict exactly what the next one will be, but it's a safe bet that it will happen again. -### [Glue](#glue) {#glue} - -This one only applies to platform authors. - -Much like with builtins, we periodically make changes to code generation or to the `roc glue` API that aren't backwards compatible. When this happens, relevant glue scripts need to be updated and then `roc glue` needs to be re-run on platforms to regenerate their host glue. - -As with builtins, it's hard to predict when these will happen and what they'll be, but right now it's a safe bet that they will happen from time to time. - ### [Import syntax](#import-syntax) {#import-syntax} Implementing the very important [module params](https://docs.google.com/document/d/110MwQi7Dpo1Y69ECFXyyvDWzF4OYv1BLojIm08qDTvg/edit?usp=sharing) feature requires a breaking syntax change to how imports work. This plan is not at all tentative; there is a high degree of confidence that it will happen! From 60ca8382a5ec2732152857917b8e2d6f4a9b6a0c Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 3 Aug 2024 11:57:23 +0200 Subject: [PATCH 143/203] improve abilities error --- crates/compiler/mono/src/ir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/mono/src/ir.rs b/crates/compiler/mono/src/ir.rs index eccd14203c0..6fbe5ad96fb 100644 --- a/crates/compiler/mono/src/ir.rs +++ b/crates/compiler/mono/src/ir.rs @@ -6140,7 +6140,7 @@ fn late_resolve_ability_specialization( member, specialization_var, ) - .expect("Ability specialization is unknown - code generation cannot proceed!"); + .expect("Ability specialization is unknown. Tip: check out "); match specialization { Resolved::Specialization(symbol) => symbol, From dd6e4b211be6bf179fc793008063710fe459d208 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 3 Aug 2024 12:37:03 +0200 Subject: [PATCH 144/203] fix typo, move comment Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- www/content/tutorial.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index 81fa684e9aa..37dfd1d8dea 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -820,14 +820,13 @@ Result.isOk (List.get ["a", "b", "c"] 1) ``` ```roc +# Running this will produce `Ok "c"` Result.try (Str.toU64 "2") listGet -listGet = U64 -> Result Str [OutOfBounds] +listGet : U64 -> Result Str [OutOfBounds] listGet = \index -> List.get ["a", "b", "c", "d"] index -# Running returns `Ok "c"` - # Notes: # - `Str.toU64 "2"` parses the string "2" to the integer 2, and returns `Ok 2` (more on # integer types later) From fd8b7355f60a03d0c49007fac4aee9bb70faf200 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 3 Aug 2024 14:37:00 +0200 Subject: [PATCH 145/203] Num.neg docs See also https://github.com/roc-lang/roc/issues/6959 Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- crates/compiler/builtins/roc/Num.roc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/compiler/builtins/roc/Num.roc b/crates/compiler/builtins/roc/Num.roc index a2d512fbb8e..46754ee1b47 100644 --- a/crates/compiler/builtins/roc/Num.roc +++ b/crates/compiler/builtins/roc/Num.roc @@ -708,6 +708,8 @@ absDiff = \a, b -> ## ## Num.neg 0.0 ## ``` +## !! Num.neg is not completely implemented for all types in all contexts, see github.com/roc-lang/roc/issues/6959 +## ## This is safe to use with any [Frac], but it can cause overflow when used with certain [Int] values. ## ## For example, calling #Num.neg on the lowest value of a signed integer (such as [Num.minI64] or [Num.minI32]) will cause overflow. From dd3d3810fb290ffee1074a3017e65d0d03e6f6b0 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 3 Aug 2024 14:43:09 +0200 Subject: [PATCH 146/203] added workaround Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- crates/compiler/builtins/roc/Num.roc | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/compiler/builtins/roc/Num.roc b/crates/compiler/builtins/roc/Num.roc index 46754ee1b47..adf526acdbf 100644 --- a/crates/compiler/builtins/roc/Num.roc +++ b/crates/compiler/builtins/roc/Num.roc @@ -709,6 +709,7 @@ absDiff = \a, b -> ## Num.neg 0.0 ## ``` ## !! Num.neg is not completely implemented for all types in all contexts, see github.com/roc-lang/roc/issues/6959 +## You can use `\someNum -> 0 - someNum` as a workaround. ## ## This is safe to use with any [Frac], but it can cause overflow when used with certain [Int] values. ## From 65ff29faa3737b29af89bfe4dab9498e20dd6d90 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 3 Aug 2024 17:08:08 +0200 Subject: [PATCH 147/203] add platform tip Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0b124012a8e..7b85d55191e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,6 +48,7 @@ Execute `cargo fmt --all` to fix the formatting. - Use a debug build of the compiler. We have many asserts enabled in the debug compiler that can alert you to something going wrong. When building from source, build the debug compiler with `cargo build --bin roc`, the binary is at roc/target/debug/roc. When using roc through a nix flake like in [basic-cli](https://github.com/roc-lang/basic-cli), use `rocPkgs.cli-debug` instead of `rocPkgs.cli`. - At the bottom of [.cargo/config.toml](https://github.com/roc-lang/roc/blob/main/.cargo/config.toml) we have useful debug flags that activate certain debug prints. - For Roc code; minimize the code that produces the issue. +- If you plan to look at the data used and produced inside the compiler, try to reproduce your issue with a very simple platform like our [minimal Rust platform](https://github.com/roc-lang/roc/tree/main/examples/platform-switching/rust-platform) instead of for example basic-cli. - For segmentation faults: + In general we recommend using linux to investigate, it has better tools for this. + Use `roc build myApp.roc --linker=legacy` followed by `valgrind ./myApp`. From ed4e8313475294f7db73ff7c1d9df6ad08b79457 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:55:53 +0200 Subject: [PATCH 148/203] fmt Num.roc --- crates/compiler/builtins/roc/Num.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/builtins/roc/Num.roc b/crates/compiler/builtins/roc/Num.roc index adf526acdbf..afdfe3b7c26 100644 --- a/crates/compiler/builtins/roc/Num.roc +++ b/crates/compiler/builtins/roc/Num.roc @@ -709,7 +709,7 @@ absDiff = \a, b -> ## Num.neg 0.0 ## ``` ## !! Num.neg is not completely implemented for all types in all contexts, see github.com/roc-lang/roc/issues/6959 -## You can use `\someNum -> 0 - someNum` as a workaround. +## You can use `\someNum -> 0 - someNum` as a workaround. ## ## This is safe to use with any [Frac], but it can cause overflow when used with certain [Int] values. ## From 70275471828e08d3e4fa2497f56703fc6debeab1 Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 6 Aug 2024 18:14:37 -0300 Subject: [PATCH 149/203] Load platform dependencies We were dropping packages from the platform header while loading. Closes #6931 --- crates/cli/tests/cli_run.rs | 11 ++ .../cli/tests/platform_requires_pkg/app.roc | 6 + .../tests/platform_requires_pkg/foo/Foo.roc | 3 + .../tests/platform_requires_pkg/foo/main.roc | 1 + .../platform_requires_pkg/platform/host.zig | 129 ++++++++++++++++++ .../platform_requires_pkg/platform/main.roc | 14 ++ crates/compiler/load_internal/src/file.rs | 3 +- 7 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 crates/cli/tests/platform_requires_pkg/app.roc create mode 100644 crates/cli/tests/platform_requires_pkg/foo/Foo.roc create mode 100644 crates/cli/tests/platform_requires_pkg/foo/main.roc create mode 100644 crates/cli/tests/platform_requires_pkg/platform/host.zig create mode 100644 crates/cli/tests/platform_requires_pkg/platform/main.roc diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index dfe61bd5ad0..d99d7ce1efa 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -711,6 +711,17 @@ mod cli_run { ) } + #[test] + #[cfg_attr(windows, ignore)] + fn platform_requires_pkg() { + test_roc_app_slim( + "crates/cli/tests/platform_requires_pkg", + "app.roc", + "from app from package", + UseValgrind::No, + ) + } + #[test] #[cfg_attr(windows, ignore)] fn transitive_expects() { diff --git a/crates/cli/tests/platform_requires_pkg/app.roc b/crates/cli/tests/platform_requires_pkg/app.roc new file mode 100644 index 00000000000..b90d12b1fa0 --- /dev/null +++ b/crates/cli/tests/platform_requires_pkg/app.roc @@ -0,0 +1,6 @@ +app [main] { + pf: platform "./platform/main.roc" +} + +main = + "from app" diff --git a/crates/cli/tests/platform_requires_pkg/foo/Foo.roc b/crates/cli/tests/platform_requires_pkg/foo/Foo.roc new file mode 100644 index 00000000000..d0591d9f275 --- /dev/null +++ b/crates/cli/tests/platform_requires_pkg/foo/Foo.roc @@ -0,0 +1,3 @@ +module [foo] + +foo = "from package" diff --git a/crates/cli/tests/platform_requires_pkg/foo/main.roc b/crates/cli/tests/platform_requires_pkg/foo/main.roc new file mode 100644 index 00000000000..ac9841cb5de --- /dev/null +++ b/crates/cli/tests/platform_requires_pkg/foo/main.roc @@ -0,0 +1 @@ +package [Foo] {} diff --git a/crates/cli/tests/platform_requires_pkg/platform/host.zig b/crates/cli/tests/platform_requires_pkg/platform/host.zig new file mode 100644 index 00000000000..1ac9ce6dbed --- /dev/null +++ b/crates/cli/tests/platform_requires_pkg/platform/host.zig @@ -0,0 +1,129 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const str = @import("glue").str; +const RocStr = str.RocStr; +const testing = std.testing; +const expectEqual = testing.expectEqual; +const expect = testing.expect; + +const Align = 2 * @alignOf(usize); +extern fn malloc(size: usize) callconv(.C) ?*align(Align) anyopaque; +extern fn realloc(c_ptr: [*]align(Align) u8, size: usize) callconv(.C) ?*anyopaque; +extern fn free(c_ptr: [*]align(Align) u8) callconv(.C) void; +extern fn memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void; +extern fn memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void; + +const DEBUG: bool = false; + +export fn roc_alloc(size: usize, alignment: u32) callconv(.C) ?*anyopaque { + if (DEBUG) { + var ptr = malloc(size); + const stdout = std.io.getStdOut().writer(); + stdout.print("alloc: {d} (alignment {d}, size {d})\n", .{ ptr, alignment, size }) catch unreachable; + return ptr; + } else { + return malloc(size); + } +} + +export fn roc_realloc(c_ptr: *anyopaque, new_size: usize, old_size: usize, alignment: u32) callconv(.C) ?*anyopaque { + if (DEBUG) { + const stdout = std.io.getStdOut().writer(); + stdout.print("realloc: {d} (alignment {d}, old_size {d})\n", .{ c_ptr, alignment, old_size }) catch unreachable; + } + + return realloc(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr))), new_size); +} + +export fn roc_dealloc(c_ptr: *anyopaque, alignment: u32) callconv(.C) void { + if (DEBUG) { + const stdout = std.io.getStdOut().writer(); + stdout.print("dealloc: {d} (alignment {d})\n", .{ c_ptr, alignment }) catch unreachable; + } + + free(@as([*]align(Align) u8, @alignCast(@ptrCast(c_ptr)))); +} + +export fn roc_panic(msg: *RocStr, tag_id: u32) callconv(.C) void { + _ = tag_id; + + const stderr = std.io.getStdErr().writer(); + stderr.print("Application crashed with message\n\n {s}\n\nShutting down\n", .{msg.asSlice()}) catch unreachable; + std.process.exit(1); +} + +export fn roc_dbg(loc: *RocStr, msg: *RocStr, src: *RocStr) callconv(.C) void { + // This platform uses stdout for testing purposes instead of the normal stderr. + const stdout = std.io.getStdOut().writer(); + stdout.print("[{s}] {s} = {s}\n", .{ loc.asSlice(), src.asSlice(), msg.asSlice() }) catch unreachable; +} + +export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void { + return memset(dst, value, size); +} + +extern fn kill(pid: c_int, sig: c_int) c_int; +extern fn shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int; +extern fn mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque; +extern fn getppid() c_int; + +fn roc_getppid() callconv(.C) c_int { + return getppid(); +} + +fn roc_getppid_windows_stub() callconv(.C) c_int { + return 0; +} + +fn roc_send_signal(pid: c_int, sig: c_int) callconv(.C) c_int { + return kill(pid, sig); +} +fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) callconv(.C) c_int { + return shm_open(name, oflag, mode); +} +fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) callconv(.C) *anyopaque { + return mmap(addr, length, prot, flags, fd, offset); +} + +comptime { + if (builtin.os.tag == .macos or builtin.os.tag == .linux) { + @export(roc_getppid, .{ .name = "roc_getppid", .linkage = .Strong }); + @export(roc_mmap, .{ .name = "roc_mmap", .linkage = .Strong }); + @export(roc_send_signal, .{ .name = "roc_send_signal", .linkage = .Strong }); + @export(roc_shm_open, .{ .name = "roc_shm_open", .linkage = .Strong }); + } + + if (builtin.os.tag == .windows) { + @export(roc_getppid_windows_stub, .{ .name = "roc_getppid", .linkage = .Strong }); + } +} + +const mem = std.mem; +const Allocator = mem.Allocator; + +extern fn roc__mainForHost_1_exposed_generic(*RocStr) void; + +const Unit = extern struct {}; + +pub fn main() u8 { + const stdout = std.io.getStdOut().writer(); + const stderr = std.io.getStdErr().writer(); + + var timer = std.time.Timer.start() catch unreachable; + + // actually call roc to populate the callresult + var callresult = RocStr.empty(); + roc__mainForHost_1_exposed_generic(&callresult); + + const nanos = timer.read(); + const seconds = (@as(f64, @floatFromInt(nanos)) / 1_000_000_000.0); + + // stdout the result + stdout.print("{s}", .{callresult.asSlice()}) catch unreachable; + + callresult.decref(); + + stderr.print("runtime: {d:.3}ms\n", .{seconds * 1000}) catch unreachable; + + return 0; +} diff --git a/crates/cli/tests/platform_requires_pkg/platform/main.roc b/crates/cli/tests/platform_requires_pkg/platform/main.roc new file mode 100644 index 00000000000..09599e5bcab --- /dev/null +++ b/crates/cli/tests/platform_requires_pkg/platform/main.roc @@ -0,0 +1,14 @@ +platform "test" + requires {} { main : _ } + exposes [] + packages { + foo: "../foo/main.roc", + } + imports [] + provides [mainForHost] + +import foo.Foo + +mainForHost : Str +mainForHost = + "$(main) $(Foo.foo)\n" diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 2e6238c9106..0676184de53 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -4922,6 +4922,7 @@ fn build_platform_header<'a>( .zip(requires.iter().copied()), arena, ); + let packages = unspace(arena, header.packages.item.items); let exposes = bumpalo::collections::Vec::from_iter_in( unspace(arena, header.exposes.item.items).iter().copied(), arena, @@ -4943,7 +4944,7 @@ fn build_platform_header<'a>( filename, is_root_module, opt_shorthand, - packages: &[], + packages, header_type, module_comments: comments, header_imports: Some(header.imports), From 8596db2055b8ae45ee34e1d5189b73502b8f27d1 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Wed, 7 Aug 2024 08:51:08 +1000 Subject: [PATCH 150/203] fix test --- crates/cli/tests/platform_requires_pkg/platform/main.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cli/tests/platform_requires_pkg/platform/main.roc b/crates/cli/tests/platform_requires_pkg/platform/main.roc index 09599e5bcab..b8c40ae927d 100644 --- a/crates/cli/tests/platform_requires_pkg/platform/main.roc +++ b/crates/cli/tests/platform_requires_pkg/platform/main.roc @@ -11,4 +11,4 @@ import foo.Foo mainForHost : Str mainForHost = - "$(main) $(Foo.foo)\n" + "$(main) $(Foo.foo)" From cb8040f629488df7f1a4c9be8a234730810b4fbf Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Sun, 4 Aug 2024 03:34:39 -0700 Subject: [PATCH 151/203] Ignore underscore-prefixed fields in record builders --- crates/cli/tests/cli/combine-tasks.roc | 4 +- crates/cli/tests/cli_run.rs | 2 +- crates/compiler/can/src/annotation.rs | 4 +- crates/compiler/can/src/def.rs | 4 +- crates/compiler/can/src/desugar.rs | 158 ++++++++++-------- crates/compiler/can/src/expr.rs | 14 +- crates/compiler/fmt/src/annotation.rs | 24 ++- crates/compiler/fmt/src/expr.rs | 17 ++ crates/compiler/load_internal/src/docs.rs | 5 +- crates/compiler/parse/src/ast.rs | 25 +-- crates/compiler/parse/src/expr.rs | 148 +++++++++++----- crates/compiler/parse/src/parser.rs | 12 +- crates/compiler/parse/src/remove_spaces.rs | 30 +++- crates/compiler/parse/src/type_annotation.rs | 3 + crates/language_server/src/analysis/tokens.rs | 3 +- crates/reporting/src/error/parse.rs | 23 ++- 16 files changed, 336 insertions(+), 140 deletions(-) diff --git a/crates/cli/tests/cli/combine-tasks.roc b/crates/cli/tests/cli/combine-tasks.roc index 70778a21d4e..0e081c46447 100644 --- a/crates/cli/tests/cli/combine-tasks.roc +++ b/crates/cli/tests/cli/combine-tasks.roc @@ -9,8 +9,8 @@ main = a: Task.ok 123, b: Task.ok "abc", c: Task.ok [123], - d: Task.ok ["abc"], - e: Task.ok (Dict.single "a" "b"), + _d: Task.ok ["abc"], + _: Task.ok (Dict.single "a" "b"), }! Stdout.line! "For multiple tasks: $(Inspect.toStr multipleIn)" diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index dfe61bd5ad0..e4d845c5ad9 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -947,7 +947,7 @@ mod cli_run { &[], &[], &[], - "For multiple tasks: {a: 123, b: \"abc\", c: [123], d: [\"abc\"], e: {\"a\": \"b\"}}\n", + "For multiple tasks: {a: 123, b: \"abc\", c: [123]}\n", UseValgrind::No, TestCliCommands::Run, ) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 2118faf72fb..54242dcd725 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -467,7 +467,8 @@ pub fn find_type_def_symbols( while let Some(assigned_field) = inner_stack.pop() { match assigned_field { AssignedField::RequiredValue(_, _, t) - | AssignedField::OptionalValue(_, _, t) => { + | AssignedField::OptionalValue(_, _, t) + | AssignedField::IgnoredValue(_, _, t) => { stack.push(&t.value); } AssignedField::LabelOnly(_) => {} @@ -1386,6 +1387,7 @@ fn can_assigned_fields<'a>( break 'inner label; } + IgnoredValue(_, _, _) => unreachable!(), LabelOnly(loc_field_name) => { // Interpret { a, b } as { a : a, b : b } let field_name = Lowercase::from(loc_field_name.value); diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 70d95a72ec7..0ae85642fef 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -631,7 +631,9 @@ fn canonicalize_claimed_ability_impl<'a>( // An error will already have been reported Err(()) } - AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => { + AssignedField::SpaceBefore(_, _) + | AssignedField::SpaceAfter(_, _) + | AssignedField::IgnoredValue(_, _, _) => { internal_error!("unreachable") } } diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index cba1b7df4dd..7818efa874e 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -521,42 +521,51 @@ pub fn desugar_expr<'a>( }); } - let mut field_names = Vec::with_capacity_in(fields.len(), arena); - let mut field_vals = Vec::with_capacity_in(fields.len(), arena); + let mut field_data = Vec::with_capacity_in(fields.len(), arena); for field in fields.items { - match desugar_field(arena, &field.value, src, line_info, module_path) { - AssignedField::RequiredValue(loc_name, _, loc_val) => { - field_names.push(loc_name); - field_vals.push(loc_val); - } - AssignedField::LabelOnly(loc_name) => { - field_names.push(loc_name); - field_vals.push(arena.alloc(Loc { - region: loc_name.region, - value: Expr::Var { - module_name: "", - ident: loc_name.value, - }, - })); - } - AssignedField::OptionalValue(loc_name, _, loc_val) => { - return arena.alloc(Loc { - region: loc_expr.region, - value: OptionalFieldInRecordBuilder(arena.alloc(loc_name), loc_val), - }); - } - AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => { - unreachable!("Should have been desugared in `desugar_field`") - } - AssignedField::Malformed(_name) => {} - } + let (name, val, ignored) = + match desugar_field(arena, &field.value, src, line_info, module_path) { + AssignedField::RequiredValue(loc_name, _, loc_val) => { + (loc_name, loc_val, false) + } + AssignedField::IgnoredValue(loc_name, _, loc_val) => { + (loc_name, loc_val, true) + } + AssignedField::LabelOnly(loc_name) => ( + loc_name, + &*arena.alloc(Loc { + region: loc_name.region, + value: Expr::Var { + module_name: "", + ident: loc_name.value, + }, + }), + false, + ), + AssignedField::OptionalValue(loc_name, _, loc_val) => { + return arena.alloc(Loc { + region: loc_expr.region, + value: OptionalFieldInRecordBuilder(arena.alloc(loc_name), loc_val), + }); + } + AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => { + unreachable!("Should have been desugared in `desugar_field`") + } + AssignedField::Malformed(_name) => continue, + }; + + field_data.push((name, val, ignored)); } - let closure_arg_from_field = |field: Loc<&'a str>| Loc { + let closure_arg_from_field = |(field, _val, ignored): (Loc<&'a str>, _, bool)| Loc { region: field.region, - value: Pattern::Identifier { - ident: arena.alloc_str(&format!("#{}", field.value)), + value: if ignored { + Pattern::Underscore(field.value) + } else { + Pattern::Identifier { + ident: arena.alloc_str(&format!("#{}", field.value)), + } }, }; @@ -607,15 +616,15 @@ pub fn desugar_expr<'a>( }; let closure_args = { - if field_names.len() == 2 { + if field_data.len() == 2 { arena.alloc_slice_copy(&[ - closure_arg_from_field(field_names[0]), - closure_arg_from_field(field_names[1]), + closure_arg_from_field(field_data[0]), + closure_arg_from_field(field_data[1]), ]) } else { let second_to_last_arg = - closure_arg_from_field(field_names[field_names.len() - 2]); - let last_arg = closure_arg_from_field(field_names[field_names.len() - 1]); + closure_arg_from_field(field_data[field_data.len() - 2]); + let last_arg = closure_arg_from_field(field_data[field_data.len() - 1]); let mut second_arg = Pattern::Tuple(Collection::with_items( arena.alloc_slice_copy(&[second_to_last_arg, last_arg]), @@ -623,18 +632,18 @@ pub fn desugar_expr<'a>( let mut second_arg_region = Region::span_across(&second_to_last_arg.region, &last_arg.region); - for index in (1..(field_names.len() - 2)).rev() { + for index in (1..(field_data.len() - 2)).rev() { second_arg = Pattern::Tuple(Collection::with_items(arena.alloc_slice_copy(&[ - closure_arg_from_field(field_names[index]), + closure_arg_from_field(field_data[index]), Loc::at(second_arg_region, second_arg), ]))); second_arg_region = - Region::span_across(&field_names[index].region, &second_arg_region); + Region::span_across(&field_data[index].0.region, &second_arg_region); } arena.alloc_slice_copy(&[ - closure_arg_from_field(field_names[0]), + closure_arg_from_field(field_data[0]), Loc::at(second_arg_region, second_arg), ]) } @@ -642,22 +651,26 @@ pub fn desugar_expr<'a>( let record_val = Record(Collection::with_items( Vec::from_iter_in( - field_names.iter().map(|field_name| { - Loc::at( - field_name.region, - AssignedField::RequiredValue( - Loc::at(field_name.region, field_name.value), - &[], - arena.alloc(Loc::at( - field_name.region, - Expr::Var { - module_name: "", - ident: arena.alloc_str(&format!("#{}", field_name.value)), - }, - )), - ), - ) - }), + field_data + .iter() + .filter(|(_name, _val, ignored)| !ignored) + .map(|(field_name, _val, _ignored)| { + Loc::at( + field_name.region, + AssignedField::RequiredValue( + Loc::at(field_name.region, field_name.value), + &[], + arena.alloc(Loc::at( + field_name.region, + Expr::Var { + module_name: "", + ident: arena + .alloc_str(&format!("#{}", field_name.value)), + }, + )), + ), + ) + }), arena, ) .into_bump_slice(), @@ -671,14 +684,14 @@ pub fn desugar_expr<'a>( ), }); - if field_names.len() == 2 { + if field_data.len() == 2 { return arena.alloc(Loc { region: loc_expr.region, value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_vals[0], - field_vals[1], + field_data[0].1, + field_data[1].1, record_combiner_closure, ]), CalledVia::RecordBuilder, @@ -688,27 +701,30 @@ pub fn desugar_expr<'a>( let mut inner_combined = arena.alloc(Loc { region: Region::span_across( - &field_vals[field_names.len() - 2].region, - &field_vals[field_names.len() - 1].region, + &field_data[field_data.len() - 2].1.region, + &field_data[field_data.len() - 1].1.region, ), value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_vals[field_names.len() - 2], - field_vals[field_names.len() - 1], + field_data[field_data.len() - 2].1, + field_data[field_data.len() - 1].1, combiner_closure_in_region(loc_expr.region), ]), CalledVia::RecordBuilder, ), }); - for index in (1..(field_names.len() - 2)).rev() { + for index in (1..(field_data.len() - 2)).rev() { inner_combined = arena.alloc(Loc { - region: Region::span_across(&field_vals[index].region, &inner_combined.region), + region: Region::span_across( + &field_data[index].1.region, + &inner_combined.region, + ), value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_vals[index], + field_data[index].1, inner_combined, combiner_closure_in_region(loc_expr.region), ]), @@ -722,7 +738,7 @@ pub fn desugar_expr<'a>( value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_vals[0], + field_data[0].1, inner_combined, record_combiner_closure, ]), @@ -1095,6 +1111,14 @@ fn desugar_field<'a>( spaces, desugar_expr(arena, loc_expr, src, line_info, module_path), ), + IgnoredValue(loc_str, spaces, loc_expr) => IgnoredValue( + Loc { + value: loc_str.value, + region: loc_str.region, + }, + spaces, + desugar_expr(arena, loc_expr, src, line_info, module_path), + ), LabelOnly(loc_str) => { // Desugar { x } into { x: x } let loc_expr = Loc { diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 5d3f3e18eb8..22f8570a53c 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1846,6 +1846,11 @@ fn canonicalize_field<'a>( field_region: Region::span_across(&label.region, &loc_expr.region), }), + // An ignored value, e.g. `{ _name: 123 }` + IgnoredValue(_, _, _) => { + internal_error!("Somehow an IgnoredValue record field was not desugared!"); + } + // A label with no value, e.g. `{ name }` (this is sugar for { name: name }) LabelOnly(_) => { internal_error!("Somehow a LabelOnly record field was not desugared!"); @@ -2433,7 +2438,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { } ast::Expr::Record(fields) => fields.iter().all(|loc_field| match loc_field.value { ast::AssignedField::RequiredValue(_label, loc_comments, loc_val) - | ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => { + | ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) + | ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => { loc_comments.is_empty() && is_valid_interpolation(&loc_val.value) } ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true, @@ -2481,7 +2487,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { is_valid_interpolation(&update.value) && fields.iter().all(|loc_field| match loc_field.value { ast::AssignedField::RequiredValue(_label, loc_comments, loc_val) - | ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => { + | ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) + | ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => { loc_comments.is_empty() && is_valid_interpolation(&loc_val.value) } ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true, @@ -2514,7 +2521,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { is_valid_interpolation(&mapper.value) && fields.iter().all(|loc_field| match loc_field.value { ast::AssignedField::RequiredValue(_label, loc_comments, loc_val) - | ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) => { + | ast::AssignedField::OptionalValue(_label, loc_comments, loc_val) + | ast::AssignedField::IgnoredValue(_label, loc_comments, loc_val) => { loc_comments.is_empty() && is_valid_interpolation(&loc_val.value) } ast::AssignedField::Malformed(_) | ast::AssignedField::LabelOnly(_) => true, diff --git a/crates/compiler/fmt/src/annotation.rs b/crates/compiler/fmt/src/annotation.rs index 25076b59406..b279a8fc9c9 100644 --- a/crates/compiler/fmt/src/annotation.rs +++ b/crates/compiler/fmt/src/annotation.rs @@ -428,9 +428,9 @@ fn is_multiline_assigned_field_help(afield: &AssignedField<'_, T use self::AssignedField::*; match afield { - RequiredValue(_, spaces, ann) | OptionalValue(_, spaces, ann) => { - !spaces.is_empty() || ann.value.is_multiline() - } + RequiredValue(_, spaces, ann) + | OptionalValue(_, spaces, ann) + | IgnoredValue(_, spaces, ann) => !spaces.is_empty() || ann.value.is_multiline(), LabelOnly(_) => false, AssignedField::SpaceBefore(_, _) | AssignedField::SpaceAfter(_, _) => true, Malformed(text) => text.chars().any(|c| c == '\n'), @@ -483,6 +483,24 @@ fn format_assigned_field_help( buf.spaces(1); ann.value.format(buf, indent); } + IgnoredValue(name, spaces, ann) => { + if is_multiline { + buf.newline(); + } + + buf.indent(indent); + buf.push('_'); + buf.push_str(name.value); + + if !spaces.is_empty() { + fmt_spaces(buf, spaces.iter(), indent); + } + + buf.spaces(separator_spaces); + buf.push(':'); + buf.spaces(1); + ann.value.format(buf, indent); + } LabelOnly(name) => { if is_multiline { buf.newline(); diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 7793bd125d8..04544ea2c6f 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -1529,6 +1529,23 @@ fn format_assigned_field_multiline( ann.value.format(buf, indent); buf.push(','); } + IgnoredValue(name, spaces, ann) => { + buf.newline(); + buf.indent(indent); + buf.push('_'); + buf.push_str(name.value); + + if !spaces.is_empty() { + fmt_spaces(buf, spaces.iter(), indent); + buf.indent(indent); + } + + buf.push_str(separator_prefix); + buf.push_str(":"); + buf.spaces(1); + ann.value.format(buf, indent); + buf.push(','); + } LabelOnly(name) => { buf.newline(); buf.indent(indent); diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index a8aa4505d87..3b4e3c8362b 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -465,7 +465,8 @@ fn contains_unexposed_type( while let Some(field) = fields_to_process.pop() { match field { AssignedField::RequiredValue(_field, _spaces, loc_val) - | AssignedField::OptionalValue(_field, _spaces, loc_val) => { + | AssignedField::OptionalValue(_field, _spaces, loc_val) + | AssignedField::IgnoredValue(_field, _spaces, loc_val) => { if contains_unexposed_type(&loc_val.value, exposed_module_ids, module_ids) { return true; } @@ -721,7 +722,7 @@ fn record_field_to_doc( AssignedField::LabelOnly(label) => Some(RecordField::LabelOnly { name: label.value.to_string(), }), - AssignedField::Malformed(_) => None, + AssignedField::Malformed(_) | AssignedField::IgnoredValue(_, _, _) => None, } } diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 36899d0d8dd..5844e18c7bc 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -681,9 +681,9 @@ fn is_when_branch_suffixed(branch: &WhenBranch<'_>) -> bool { fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool { match value { - AssignedField::RequiredValue(_, _, a) | AssignedField::OptionalValue(_, _, a) => { - is_expr_suffixed(&a.value) - } + AssignedField::RequiredValue(_, _, a) + | AssignedField::OptionalValue(_, _, a) + | AssignedField::IgnoredValue(_, _, a) => is_expr_suffixed(&a.value), AssignedField::LabelOnly(_) => false, AssignedField::SpaceBefore(a, _) | AssignedField::SpaceAfter(a, _) => { is_assigned_value_suffixed(a) @@ -869,9 +869,9 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { use AssignedField::*; match current { - RequiredValue(_, _, loc_val) | OptionalValue(_, _, loc_val) => { - break expr_stack.push(&loc_val.value) - } + RequiredValue(_, _, loc_val) + | OptionalValue(_, _, loc_val) + | IgnoredValue(_, _, loc_val) => break expr_stack.push(&loc_val.value), SpaceBefore(next, _) | SpaceAfter(next, _) => current = *next, LabelOnly(_) | Malformed(_) => break, } @@ -1598,6 +1598,9 @@ pub enum AssignedField<'a, Val> { // and in destructuring patterns (e.g. `{ name ? "blah" }`) OptionalValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc), + // An ignored field, e.g. `{ _name: "blah" }` or `{ _ : Str }` + IgnoredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc), + // A label with no value, e.g. `{ name }` (this is sugar for { name: name }) LabelOnly(Loc<&'a str>), @@ -1615,7 +1618,9 @@ impl<'a, Val> AssignedField<'a, Val> { loop { match current { - Self::RequiredValue(_, _, val) | Self::OptionalValue(_, _, val) => break Some(val), + Self::RequiredValue(_, _, val) + | Self::OptionalValue(_, _, val) + | Self::IgnoredValue(_, _, val) => break Some(val), Self::LabelOnly(_) | Self::Malformed(_) => break None, Self::SpaceBefore(next, _) | Self::SpaceAfter(next, _) => current = *next, } @@ -2577,9 +2582,9 @@ impl Malformed for Option { impl<'a, T: Malformed> Malformed for AssignedField<'a, T> { fn is_malformed(&self) -> bool { match self { - AssignedField::RequiredValue(_, _, val) | AssignedField::OptionalValue(_, _, val) => { - val.is_malformed() - } + AssignedField::RequiredValue(_, _, val) + | AssignedField::OptionalValue(_, _, val) + | AssignedField::IgnoredValue(_, _, val) => val.is_malformed(), AssignedField::LabelOnly(_) => false, AssignedField::SpaceBefore(field, _) | AssignedField::SpaceAfter(field, _) => { field.is_malformed() diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 03a4534cb30..1e5036c4981 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -914,6 +914,10 @@ fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams< let params = record.fields.map_items_result(arena, |loc_field| { match loc_field.value.to_assigned_field(arena) { + Ok(AssignedField::IgnoredValue(_, _, _)) => Err(( + MadeProgress, + EImportParams::RecordIgnoredFieldFound(loc_field.region), + )), Ok(field) => Ok(Loc::at(loc_field.region, field)), Err(FoundApplyValue) => Err(( MadeProgress, @@ -2234,6 +2238,7 @@ fn assigned_expr_field_to_pattern_help<'a>( spaces, ), AssignedField::Malformed(string) => Pattern::Malformed(string), + AssignedField::IgnoredValue(_, _, _) => return Err(()), }) } @@ -3322,6 +3327,7 @@ fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> { pub enum RecordField<'a> { RequiredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc>), OptionalValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc>), + IgnoredValue(Loc<&'a str>, &'a [CommentOrNewline<'a>], &'a Loc>), LabelOnly(Loc<&'a str>), SpaceBefore(&'a RecordField<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a RecordField<'a>, &'a [CommentOrNewline<'a>]), @@ -3337,7 +3343,10 @@ pub enum RecordField<'a> { pub struct FoundApplyValue; #[derive(Debug)] -struct FoundOptionalValue; +pub enum NotOldBuilderFieldValue { + FoundOptionalValue, + FoundIgnoredValue, +} impl<'a> RecordField<'a> { fn is_apply_value(&self) -> bool { @@ -3354,6 +3363,20 @@ impl<'a> RecordField<'a> { } } + fn is_ignored_value(&self) -> bool { + let mut current = self; + + loop { + match current { + RecordField::IgnoredValue(_, _, _) => break true, + RecordField::SpaceBefore(field, _) | RecordField::SpaceAfter(field, _) => { + current = *field; + } + _ => break false, + } + } + } + pub fn to_assigned_field( self, arena: &'a Bump, @@ -3369,6 +3392,10 @@ impl<'a> RecordField<'a> { Ok(OptionalValue(loc_label, spaces, loc_expr)) } + RecordField::IgnoredValue(loc_label, spaces, loc_expr) => { + Ok(IgnoredValue(loc_label, spaces, loc_expr)) + } + RecordField::LabelOnly(loc_label) => Ok(LabelOnly(loc_label)), RecordField::ApplyValue(_, _, _, _) => Err(FoundApplyValue), @@ -3390,7 +3417,7 @@ impl<'a> RecordField<'a> { fn to_builder_field( self, arena: &'a Bump, - ) -> Result, FoundOptionalValue> { + ) -> Result, NotOldBuilderFieldValue> { use OldRecordBuilderField::*; match self { @@ -3398,7 +3425,9 @@ impl<'a> RecordField<'a> { Ok(Value(loc_label, spaces, loc_expr)) } - RecordField::OptionalValue(_, _, _) => Err(FoundOptionalValue), + RecordField::OptionalValue(_, _, _) => Err(NotOldBuilderFieldValue::FoundOptionalValue), + + RecordField::IgnoredValue(_, _, _) => Err(NotOldBuilderFieldValue::FoundIgnoredValue), RecordField::LabelOnly(loc_label) => Ok(LabelOnly(loc_label)), @@ -3434,42 +3463,70 @@ pub fn record_field<'a>() -> impl Parser<'a, RecordField<'a>, ERecord<'a>> { use RecordField::*; map_with_arena( - and( - specialize_err(|_, pos| ERecord::Field(pos), loc(lowercase_ident())), + either( and( - spaces(), - optional(either( - and(byte(b':', ERecord::Colon), record_field_expr()), - and( - byte(b'?', ERecord::QuestionMark), + specialize_err(|_, pos| ERecord::Field(pos), loc(lowercase_ident())), + and( + spaces(), + optional(either( + and(byte(b':', ERecord::Colon), record_field_expr()), + and( + byte(b'?', ERecord::QuestionMark), + spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))), + ), + )), + ), + ), + and( + loc(skip_first( + byte(b'_', ERecord::UnderscoreField), + optional(specialize_err( + |_, pos| ERecord::Field(pos), + lowercase_ident(), + )), + )), + and( + spaces(), + skip_first( + byte(b':', ERecord::Colon), spaces_before(specialize_err_ref(ERecord::Expr, loc_expr(false))), ), - )), + ), ), ), - |arena: &'a bumpalo::Bump, (loc_label, (spaces, opt_loc_val))| { - match opt_loc_val { - Some(Either::First((_, RecordFieldExpr::Value(loc_val)))) => { - RequiredValue(loc_label, spaces, arena.alloc(loc_val)) - } + |arena: &'a bumpalo::Bump, field_data| { + match field_data { + Either::First((loc_label, (spaces, opt_loc_val))) => { + match opt_loc_val { + Some(Either::First((_, RecordFieldExpr::Value(loc_val)))) => { + RequiredValue(loc_label, spaces, arena.alloc(loc_val)) + } - Some(Either::First((_, RecordFieldExpr::Apply(arrow_spaces, loc_val)))) => { - ApplyValue(loc_label, spaces, arrow_spaces, arena.alloc(loc_val)) - } + Some(Either::First((_, RecordFieldExpr::Apply(arrow_spaces, loc_val)))) => { + ApplyValue(loc_label, spaces, arrow_spaces, arena.alloc(loc_val)) + } - Some(Either::Second((_, loc_val))) => { - OptionalValue(loc_label, spaces, arena.alloc(loc_val)) - } + Some(Either::Second((_, loc_val))) => { + OptionalValue(loc_label, spaces, arena.alloc(loc_val)) + } - // If no value was provided, record it as a Var. - // Canonicalize will know what to do with a Var later. - None => { - if !spaces.is_empty() { - SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces) - } else { - LabelOnly(loc_label) + // If no value was provided, record it as a Var. + // Canonicalize will know what to do with a Var later. + None => { + if !spaces.is_empty() { + SpaceAfter(arena.alloc(LabelOnly(loc_label)), spaces) + } else { + LabelOnly(loc_label) + } + } } } + Either::Second((loc_opt_label, (spaces, loc_val))) => { + let loc_label = loc_opt_label + .map(|opt_label| opt_label.unwrap_or_else(|| arena.alloc_str(""))); + + IgnoredValue(loc_label, spaces, arena.alloc(loc_val)) + } } }, ) @@ -3573,20 +3630,23 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> { new_record_builder_help(arena, mapper, record.fields) } None => { - let is_old_record_builder = record - .fields - .iter() - .any(|field| field.value.is_apply_value()); + let special_field_found = record.fields.iter().find_map(|field| { + if field.value.is_apply_value() { + Some(old_record_builder_help(arena, record.fields)) + } else if field.value.is_ignored_value() { + Some(Err(EExpr::RecordUpdateIgnoredField(field.region))) + } else { + None + } + }); - if is_old_record_builder { - old_record_builder_help(arena, record.fields) - } else { + special_field_found.unwrap_or_else(|| { let fields = record.fields.map_items(arena, |loc_field| { loc_field.map(|field| field.to_assigned_field(arena).unwrap()) }); Ok(Expr::Record(fields)) - } + }) } }; @@ -3609,11 +3669,14 @@ fn record_update_help<'a>( ) -> Result, EExpr<'a>> { let result = fields.map_items_result(arena, |loc_field| { match loc_field.value.to_assigned_field(arena) { + Ok(AssignedField::IgnoredValue(_, _, _)) => { + Err(EExpr::RecordUpdateIgnoredField(loc_field.region)) + } Ok(builder_field) => Ok(Loc { region: loc_field.region, value: builder_field, }), - Err(FoundApplyValue) => Err(EExpr::RecordUpdateAccumulator(loc_field.region)), + Err(FoundApplyValue) => Err(EExpr::RecordUpdateOldBuilderField(loc_field.region)), } }); @@ -3634,7 +3697,7 @@ fn new_record_builder_help<'a>( region: loc_field.region, value: builder_field, }), - Err(FoundApplyValue) => Err(EExpr::RecordBuilderAccumulator(loc_field.region)), + Err(FoundApplyValue) => Err(EExpr::RecordBuilderOldBuilderField(loc_field.region)), } }); @@ -3654,7 +3717,12 @@ fn old_record_builder_help<'a>( region: loc_field.region, value: builder_field, }), - Err(FoundOptionalValue) => Err(EExpr::OptionalValueInRecordBuilder(loc_field.region)), + Err(NotOldBuilderFieldValue::FoundOptionalValue) => { + Err(EExpr::OptionalValueInOldRecordBuilder(loc_field.region)) + } + Err(NotOldBuilderFieldValue::FoundIgnoredValue) => { + Err(EExpr::IgnoredValueInOldRecordBuilder(loc_field.region)) + } } }); diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index 7eec1b83e9a..c773acab730 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -372,9 +372,11 @@ pub enum EExpr<'a> { InParens(EInParens<'a>, Position), Record(ERecord<'a>, Position), - OptionalValueInRecordBuilder(Region), - RecordUpdateAccumulator(Region), - RecordBuilderAccumulator(Region), + OptionalValueInOldRecordBuilder(Region), + IgnoredValueInOldRecordBuilder(Region), + RecordUpdateOldBuilderField(Region), + RecordUpdateIgnoredField(Region), + RecordBuilderOldBuilderField(Region), // SingleQuote errors are folded into the EString Str(EString<'a>, Position), @@ -428,6 +430,7 @@ pub enum ERecord<'a> { Prefix(Position), Field(Position), + UnderscoreField(Position), Colon(Position), QuestionMark(Position), Arrow(Position), @@ -577,6 +580,7 @@ pub enum EImportParams<'a> { RecordUpdateFound(Region), RecordBuilderFound(Region), RecordApplyFound(Region), + RecordIgnoredFieldFound(Region), Space(BadInputError, Position), } @@ -735,6 +739,7 @@ pub enum ETypeAbilityImpl<'a> { Open(Position), Field(Position), + UnderscoreField(Position), Colon(Position), Arrow(Position), Optional(Position), @@ -756,6 +761,7 @@ impl<'a> From> for ETypeAbilityImpl<'a> { ERecord::End(p) => ETypeAbilityImpl::End(p), ERecord::Open(p) => ETypeAbilityImpl::Open(p), ERecord::Field(p) => ETypeAbilityImpl::Field(p), + ERecord::UnderscoreField(p) => ETypeAbilityImpl::UnderscoreField(p), ERecord::Colon(p) => ETypeAbilityImpl::Colon(p), ERecord::Arrow(p) => ETypeAbilityImpl::Arrow(p), ERecord::Space(s, p) => ETypeAbilityImpl::Space(s, p), diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index 1de484706e7..67178b652bd 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -543,6 +543,11 @@ impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for Assi arena.alloc([]), arena.alloc(c.remove_spaces(arena)), ), + AssignedField::IgnoredValue(a, _, c) => AssignedField::IgnoredValue( + a.remove_spaces(arena), + arena.alloc([]), + arena.alloc(c.remove_spaces(arena)), + ), AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)), AssignedField::Malformed(a) => AssignedField::Malformed(a), AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena), @@ -983,8 +988,11 @@ impl<'a> RemoveSpaces<'a> for EExpr<'a> { EExpr::Record(inner_err, _pos) => { EExpr::Record(inner_err.remove_spaces(arena), Position::zero()) } - EExpr::OptionalValueInRecordBuilder(_pos) => { - EExpr::OptionalValueInRecordBuilder(Region::zero()) + EExpr::OptionalValueInOldRecordBuilder(_pos) => { + EExpr::OptionalValueInOldRecordBuilder(Region::zero()) + } + EExpr::IgnoredValueInOldRecordBuilder(_pos) => { + EExpr::OptionalValueInOldRecordBuilder(Region::zero()) } EExpr::Str(inner_err, _pos) => { EExpr::Str(inner_err.remove_spaces(arena), Position::zero()) @@ -998,8 +1006,15 @@ impl<'a> RemoveSpaces<'a> for EExpr<'a> { EExpr::UnexpectedComma(_pos) => EExpr::UnexpectedComma(Position::zero()), EExpr::UnexpectedTopLevelExpr(_pos) => EExpr::UnexpectedTopLevelExpr(Position::zero()), EExpr::StmtAfterExpr(_pos) => EExpr::StmtAfterExpr(Position::zero()), - EExpr::RecordUpdateAccumulator(_) => EExpr::RecordUpdateAccumulator(Region::zero()), - EExpr::RecordBuilderAccumulator(_) => EExpr::RecordBuilderAccumulator(Region::zero()), + EExpr::RecordUpdateOldBuilderField(_pos) => { + EExpr::RecordUpdateOldBuilderField(Region::zero()) + } + EExpr::RecordUpdateIgnoredField(_pos) => { + EExpr::RecordUpdateIgnoredField(Region::zero()) + } + EExpr::RecordBuilderOldBuilderField(_pos) => { + EExpr::RecordBuilderOldBuilderField(Region::zero()) + } } } } @@ -1089,6 +1104,7 @@ impl<'a> RemoveSpaces<'a> for ERecord<'a> { ERecord::End(_) => ERecord::End(Position::zero()), ERecord::Open(_) => ERecord::Open(Position::zero()), ERecord::Field(_pos) => ERecord::Field(Position::zero()), + ERecord::UnderscoreField(_pos) => ERecord::Field(Position::zero()), ERecord::Colon(_) => ERecord::Colon(Position::zero()), ERecord::QuestionMark(_) => ERecord::QuestionMark(Position::zero()), ERecord::Arrow(_) => ERecord::Arrow(Position::zero()), @@ -1217,6 +1233,9 @@ impl<'a> RemoveSpaces<'a> for EImportParams<'a> { } EImportParams::RecordUpdateFound(_) => EImportParams::RecordUpdateFound(Region::zero()), EImportParams::RecordApplyFound(_) => EImportParams::RecordApplyFound(Region::zero()), + EImportParams::RecordIgnoredFieldFound(_) => { + EImportParams::RecordIgnoredFieldFound(Region::zero()) + } EImportParams::Space(inner_err, _) => { EImportParams::Space(*inner_err, Position::zero()) } @@ -1248,6 +1267,9 @@ impl<'a> RemoveSpaces<'a> for ETypeAbilityImpl<'a> { ETypeAbilityImpl::End(_) => ETypeAbilityImpl::End(Position::zero()), ETypeAbilityImpl::Open(_) => ETypeAbilityImpl::Open(Position::zero()), ETypeAbilityImpl::Field(_) => ETypeAbilityImpl::Field(Position::zero()), + ETypeAbilityImpl::UnderscoreField(_) => { + ETypeAbilityImpl::UnderscoreField(Position::zero()) + } ETypeAbilityImpl::Colon(_) => ETypeAbilityImpl::Colon(Position::zero()), ETypeAbilityImpl::Arrow(_) => ETypeAbilityImpl::Arrow(Position::zero()), ETypeAbilityImpl::Optional(_) => ETypeAbilityImpl::Optional(Position::zero()), diff --git a/crates/compiler/parse/src/type_annotation.rs b/crates/compiler/parse/src/type_annotation.rs index 5bbc28903cd..2802a902e26 100644 --- a/crates/compiler/parse/src/type_annotation.rs +++ b/crates/compiler/parse/src/type_annotation.rs @@ -565,6 +565,9 @@ fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, ETyp fn ability_impl_field<'a>() -> impl Parser<'a, AssignedField<'a, Expr<'a>>, ERecord<'a>> { then(record_field(), move |arena, state, _, field| { match field.to_assigned_field(arena) { + Ok(AssignedField::IgnoredValue(_, _, _)) => { + Err((MadeProgress, ERecord::Field(state.pos()))) + } Ok(assigned_field) => Ok((MadeProgress, assigned_field, state)), Err(FoundApplyValue) => Err((MadeProgress, ERecord::Field(state.pos()))), } diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 982d67c612a..f297e0e5788 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -446,7 +446,8 @@ where fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc> { match self { AssignedField::RequiredValue(field, _, ty) - | AssignedField::OptionalValue(field, _, ty) => (field_token(field.region, arena) + | AssignedField::OptionalValue(field, _, ty) + | AssignedField::IgnoredValue(field, _, ty) => (field_token(field.region, arena) .into_iter()) .chain(ty.iter_tokens(arena)) .collect_in(arena), diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index c37482cfb67..e801da8aabe 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -545,7 +545,7 @@ fn to_expr_report<'a>( to_record_report(alloc, lines, filename, erecord, *pos, start) } - EExpr::OptionalValueInRecordBuilder(region) => { + EExpr::OptionalValueInOldRecordBuilder(region) => { let surroundings = Region::new(start, region.end()); let region = lines.convert_region(*region); @@ -565,7 +565,7 @@ fn to_expr_report<'a>( } } - EExpr::RecordUpdateAccumulator(region) => { + EExpr::RecordUpdateOldBuilderField(region) => { let surroundings = Region::new(start, region.end()); let region = lines.convert_region(*region); @@ -1580,6 +1580,25 @@ fn to_import_report<'a>( severity, } } + Params(EImportParams::RecordIgnoredFieldFound(region), _) => { + let surroundings = Region::new(start, region.end()); + let region = lines.convert_region(*region); + + let doc = alloc.stack([ + alloc.reflow("I was partway through parsing module params, but I got stuck here:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), + alloc.reflow( + "This is an ignored record field, but those are not allowed in module params.", + ), + ]); + + Report { + filename, + doc, + title: "IGNORED RECORD FIELD IN MODULE PARAMS".to_string(), + severity, + } + } Params(EImportParams::RecordUpdateFound(region), _) => { let surroundings = Region::new(start, region.end()); let region = lines.convert_region(*region); From 00bc699642f60a2c446ffdcdaa7c769d5f09b719 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Tue, 6 Aug 2024 00:51:09 -0700 Subject: [PATCH 152/203] Add syntax tests for record builder --- crates/compiler/can/src/desugar.rs | 83 +++++++++++-------- .../pass/record_builder.expr.formatted.roc | 4 + .../pass/record_builder.expr.result-ast | 32 +++++++ .../snapshots/pass/record_builder.expr.roc | 2 + ..._builder_ignored_fields.expr.formatted.roc | 6 ++ ...ord_builder_ignored_fields.expr.result-ast | 46 ++++++++++ .../record_builder_ignored_fields.expr.roc | 2 + .../test_syntax/tests/test_snapshots.rs | 2 + 8 files changed, 143 insertions(+), 34 deletions(-) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.roc diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 7818efa874e..2056178b0d2 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -521,10 +521,16 @@ pub fn desugar_expr<'a>( }); } + struct FieldData<'d> { + name: Loc<&'d str>, + value: &'d Loc>, + ignored: bool, + } + let mut field_data = Vec::with_capacity_in(fields.len(), arena); for field in fields.items { - let (name, val, ignored) = + let (name, value, ignored) = match desugar_field(arena, &field.value, src, line_info, module_path) { AssignedField::RequiredValue(loc_name, _, loc_val) => { (loc_name, loc_val, false) @@ -555,19 +561,28 @@ pub fn desugar_expr<'a>( AssignedField::Malformed(_name) => continue, }; - field_data.push((name, val, ignored)); + field_data.push(FieldData { + name, + value, + ignored, + }); } - let closure_arg_from_field = |(field, _val, ignored): (Loc<&'a str>, _, bool)| Loc { - region: field.region, - value: if ignored { - Pattern::Underscore(field.value) - } else { - Pattern::Identifier { - ident: arena.alloc_str(&format!("#{}", field.value)), - } - }, - }; + let closure_arg_from_field = + |FieldData { + name, + value: _, + ignored, + }: &FieldData<'a>| Loc { + region: name.region, + value: if *ignored { + Pattern::Underscore(name.value) + } else { + Pattern::Identifier { + ident: arena.alloc_str(&format!("#{}", name.value)), + } + }, + }; let combiner_closure_in_region = |region| { let closure_body = Tuple(Collection::with_items( @@ -618,13 +633,13 @@ pub fn desugar_expr<'a>( let closure_args = { if field_data.len() == 2 { arena.alloc_slice_copy(&[ - closure_arg_from_field(field_data[0]), - closure_arg_from_field(field_data[1]), + closure_arg_from_field(&field_data[0]), + closure_arg_from_field(&field_data[1]), ]) } else { let second_to_last_arg = - closure_arg_from_field(field_data[field_data.len() - 2]); - let last_arg = closure_arg_from_field(field_data[field_data.len() - 1]); + closure_arg_from_field(&field_data[field_data.len() - 2]); + let last_arg = closure_arg_from_field(&field_data[field_data.len() - 1]); let mut second_arg = Pattern::Tuple(Collection::with_items( arena.alloc_slice_copy(&[second_to_last_arg, last_arg]), @@ -635,15 +650,15 @@ pub fn desugar_expr<'a>( for index in (1..(field_data.len() - 2)).rev() { second_arg = Pattern::Tuple(Collection::with_items(arena.alloc_slice_copy(&[ - closure_arg_from_field(field_data[index]), + closure_arg_from_field(&field_data[index]), Loc::at(second_arg_region, second_arg), ]))); second_arg_region = - Region::span_across(&field_data[index].0.region, &second_arg_region); + Region::span_across(&field_data[index].name.region, &second_arg_region); } arena.alloc_slice_copy(&[ - closure_arg_from_field(field_data[0]), + closure_arg_from_field(&field_data[0]), Loc::at(second_arg_region, second_arg), ]) } @@ -653,19 +668,19 @@ pub fn desugar_expr<'a>( Vec::from_iter_in( field_data .iter() - .filter(|(_name, _val, ignored)| !ignored) - .map(|(field_name, _val, _ignored)| { + .filter(|field| !field.ignored) + .map(|field| { Loc::at( - field_name.region, + field.name.region, AssignedField::RequiredValue( - Loc::at(field_name.region, field_name.value), + field.name, &[], arena.alloc(Loc::at( - field_name.region, + field.name.region, Expr::Var { module_name: "", ident: arena - .alloc_str(&format!("#{}", field_name.value)), + .alloc_str(&format!("#{}", field.name.value)), }, )), ), @@ -690,8 +705,8 @@ pub fn desugar_expr<'a>( value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_data[0].1, - field_data[1].1, + field_data[0].value, + field_data[1].value, record_combiner_closure, ]), CalledVia::RecordBuilder, @@ -701,14 +716,14 @@ pub fn desugar_expr<'a>( let mut inner_combined = arena.alloc(Loc { region: Region::span_across( - &field_data[field_data.len() - 2].1.region, - &field_data[field_data.len() - 1].1.region, + &field_data[field_data.len() - 2].value.region, + &field_data[field_data.len() - 1].value.region, ), value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_data[field_data.len() - 2].1, - field_data[field_data.len() - 1].1, + field_data[field_data.len() - 2].value, + field_data[field_data.len() - 1].value, combiner_closure_in_region(loc_expr.region), ]), CalledVia::RecordBuilder, @@ -718,13 +733,13 @@ pub fn desugar_expr<'a>( for index in (1..(field_data.len() - 2)).rev() { inner_combined = arena.alloc(Loc { region: Region::span_across( - &field_data[index].1.region, + &field_data[index].value.region, &inner_combined.region, ), value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_data[index].1, + field_data[index].value, inner_combined, combiner_closure_in_region(loc_expr.region), ]), @@ -738,7 +753,7 @@ pub fn desugar_expr<'a>( value: Apply( new_mapper, arena.alloc_slice_copy(&[ - field_data[0].1, + field_data[0].value, inner_combined, record_combiner_closure, ]), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.formatted.roc new file mode 100644 index 00000000000..911a0f8c043 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.formatted.roc @@ -0,0 +1,4 @@ +{ Foo.Bar.baz <- + x: 5, + y: 0, +} \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.result-ast new file mode 100644 index 00000000000..b3d94466f17 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.result-ast @@ -0,0 +1,32 @@ +SpaceAfter( + RecordBuilder { + mapper: @2-13 Var { + module_name: "Foo.Bar", + ident: "baz", + }, + fields: [ + @17-21 RequiredValue( + @17-18 "x", + [], + @20-21 Num( + "5", + ), + ), + @23-27 SpaceAfter( + RequiredValue( + @23-24 "y", + [], + @26-27 Num( + "0", + ), + ), + [ + Newline, + ], + ), + ], + }, + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.roc new file mode 100644 index 00000000000..9a075a9e5c4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder.expr.roc @@ -0,0 +1,2 @@ +{ Foo.Bar.baz <- x: 5, y: 0 +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.formatted.roc new file mode 100644 index 00000000000..2bf71d9a449 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.formatted.roc @@ -0,0 +1,6 @@ +{ Foo.Bar.baz <- + x: 5, + y: 0, + _z: 3, + _: 2, +} \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.result-ast new file mode 100644 index 00000000000..af37c9c12e6 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.result-ast @@ -0,0 +1,46 @@ +SpaceAfter( + RecordBuilder { + mapper: @2-13 Var { + module_name: "Foo.Bar", + ident: "baz", + }, + fields: [ + @17-21 RequiredValue( + @17-18 "x", + [], + @20-21 Num( + "5", + ), + ), + @23-27 RequiredValue( + @23-24 "y", + [], + @26-27 Num( + "0", + ), + ), + @29-34 IgnoredValue( + @29-31 "z", + [], + @33-34 Num( + "3", + ), + ), + @36-40 SpaceAfter( + IgnoredValue( + @36-37 "", + [], + @39-40 Num( + "2", + ), + ), + [ + Newline, + ], + ), + ], + }, + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.roc new file mode 100644 index 00000000000..f42ce745668 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_builder_ignored_fields.expr.roc @@ -0,0 +1,2 @@ +{ Foo.Bar.baz <- x: 5, y: 0, _z: 3, _: 2 +} diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 27736ae7a61..44fb528d4cb 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -449,6 +449,8 @@ mod test_snapshots { pass/qualified_field.expr, pass/qualified_var.expr, pass/record_access_after_tuple.expr, + pass/record_builder.expr, + pass/record_builder_ignored_fields.expr, pass/record_destructure_def.expr, pass/record_func_type_decl.expr, pass/record_type_with_function.expr, From d25c048d4856676de0af9218f2f455d075123d84 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Tue, 6 Aug 2024 19:03:08 -0700 Subject: [PATCH 153/203] Move Full from fmt to parse and reorganize confusingly-named Module ast type --- crates/cli/src/format.rs | 27 +- .../compiler/fmt/src/{module.rs => header.rs} | 8 +- crates/compiler/fmt/src/lib.rs | 9 +- crates/compiler/fmt/src/spaces.rs | 14 +- crates/compiler/load/tests/test_reporting.rs | 20 +- crates/compiler/load_internal/src/file.rs | 76 +- crates/compiler/parse/benches/bench_parse.rs | 6 +- crates/compiler/parse/src/ast.rs | 32 +- crates/compiler/parse/src/expr.rs | 10 +- crates/compiler/parse/src/header.rs | 950 +++++++++++++++++- crates/compiler/parse/src/highlight.rs | 2 +- crates/compiler/parse/src/lib.rs | 1 - crates/compiler/parse/src/module.rs | 945 ----------------- crates/compiler/parse/src/remove_spaces.rs | 38 +- crates/compiler/parse/src/test_helpers.rs | 8 +- crates/compiler/parse/tests/test_parse.rs | 2 +- .../compiler/test_syntax/src/test_helpers.rs | 52 +- .../pass/empty_app_header.header.result-ast | 6 +- .../empty_hosted_header.header.result-ast | 6 +- .../empty_module_header.header.result-ast | 6 +- .../empty_package_header.header.result-ast | 6 +- .../empty_platform_header.header.result-ast | 6 +- .../pass/full_app_header.header.result-ast | 6 +- ...p_header_trailing_commas.header.result-ast | 6 +- .../function_effect_types.header.result-ast | 6 +- .../pass/minimal_app_header.header.result-ast | 6 +- ...module_multiline_exposes.header.result-ast | 6 +- ...iline_params_and_exposes.header.result-ast | 6 +- .../module_with_newline.header.result-ast | 6 +- ...dule_with_optional_param.header.result-ast | 6 +- .../pass/module_with_params.header.result-ast | 6 +- ...ms_and_multiline_exposes.header.result-ast | 6 +- .../pass/newline_in_packages.full.result-ast | 148 +-- .../nonempty_hosted_header.header.result-ast | 6 +- .../nonempty_package_header.header.result-ast | 6 +- ...nonempty_platform_header.header.result-ast | 6 +- .../pass/old_app_header.full.result-ast | 210 ++-- .../old_interface_header.header.result-ast | 6 +- .../pass/provides_type.header.result-ast | 6 +- .../pass/requires_type.header.result-ast | 6 +- .../pass/space_before_colon.full.result-ast | 132 +-- .../pass/suffixed_one_def.full.result-ast | 368 +++---- .../suffixed_optional_last.full.result-ast | 230 ++--- crates/compiler/test_syntax/tests/test_fmt.rs | 14 +- .../language_server/src/analysis/parse_ast.rs | 19 +- crates/language_server/src/analysis/tokens.rs | 14 +- crates/packaging/src/tarball.rs | 12 +- 47 files changed, 1728 insertions(+), 1745 deletions(-) rename crates/compiler/fmt/src/{module.rs => header.rs} (98%) delete mode 100644 crates/compiler/parse/src/module.rs diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index 7d33833fb4c..2eec0782b1d 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -5,11 +5,12 @@ use std::path::{Path, PathBuf}; use bumpalo::Bump; use roc_error_macros::{internal_error, user_error}; use roc_fmt::def::fmt_defs; -use roc_fmt::module::fmt_module; -use roc_fmt::{Ast, Buf}; -use roc_parse::module::parse_module_defs; +use roc_fmt::header::fmt_header; +use roc_fmt::Buf; +use roc_parse::ast::{Full, SpacesBefore}; +use roc_parse::header::parse_module_defs; use roc_parse::remove_spaces::RemoveSpaces; -use roc_parse::{module, parser::SyntaxError, state::State}; +use roc_parse::{header, parser::SyntaxError, state::State}; #[derive(Copy, Clone, Debug)] pub enum FormatMode { @@ -230,19 +231,25 @@ pub fn format_src(arena: &Bump, src: &str) -> Result { Ok(buf.as_str().to_string()) } -fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError<'a>> { - let (module, state) = module::parse_header(arena, State::new(src.as_bytes())) +fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError<'a>> { + let (header, state) = header::parse_header(arena, State::new(src.as_bytes())) .map_err(|e| SyntaxError::Header(e.problem))?; - let (module, defs) = module.upgrade_header_imports(arena); + let (h, defs) = header.item.upgrade_header_imports(arena); let defs = parse_module_defs(arena, state, defs)?; - Ok(Ast { module, defs }) + Ok(Full { + header: SpacesBefore { + before: header.before, + item: h, + }, + defs, + }) } -fn fmt_all<'a>(buf: &mut Buf<'a>, ast: &'a Ast) { - fmt_module(buf, &ast.module); +fn fmt_all<'a>(buf: &mut Buf<'a>, ast: &'a Full) { + fmt_header(buf, &ast.header); fmt_defs(buf, &ast.defs, 0); diff --git a/crates/compiler/fmt/src/module.rs b/crates/compiler/fmt/src/header.rs similarity index 98% rename from crates/compiler/fmt/src/module.rs rename to crates/compiler/fmt/src/header.rs index 76568f85af0..83b83f34941 100644 --- a/crates/compiler/fmt/src/module.rs +++ b/crates/compiler/fmt/src/header.rs @@ -5,7 +5,7 @@ use crate::collection::{fmt_collection, Braces}; use crate::expr::fmt_str_literal; use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT}; use crate::Buf; -use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces}; +use roc_parse::ast::{Collection, CommentOrNewline, Header, Spaced, Spaces, SpacesBefore}; use roc_parse::header::{ AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, ImportsKeyword, Keyword, KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, @@ -16,9 +16,9 @@ use roc_parse::header::{ use roc_parse::ident::UppercaseIdent; use roc_region::all::Loc; -pub fn fmt_module<'a>(buf: &mut Buf<'_>, module: &'a Module<'a>) { - fmt_comments_only(buf, module.comments.iter(), NewlineAt::Bottom, 0); - match &module.header { +pub fn fmt_header<'a>(buf: &mut Buf<'_>, header: &'a SpacesBefore<'a, Header<'a>>) { + fmt_comments_only(buf, header.before.iter(), NewlineAt::Bottom, 0); + match &header.item { Header::Module(header) => { fmt_module_header(buf, header); } diff --git a/crates/compiler/fmt/src/lib.rs b/crates/compiler/fmt/src/lib.rs index 88a4974bf02..d92b0152ad7 100644 --- a/crates/compiler/fmt/src/lib.rs +++ b/crates/compiler/fmt/src/lib.rs @@ -6,18 +6,11 @@ pub mod annotation; pub mod collection; pub mod def; pub mod expr; -pub mod module; +pub mod header; pub mod pattern; pub mod spaces; use bumpalo::{collections::String, Bump}; -use roc_parse::ast::Module; - -#[derive(Debug)] -pub struct Ast<'a> { - pub module: Module<'a>, - pub defs: roc_parse::ast::Defs<'a>, -} #[derive(Debug)] pub struct Buf<'a> { diff --git a/crates/compiler/fmt/src/spaces.rs b/crates/compiler/fmt/src/spaces.rs index e794a3c67a9..7f56ab71769 100644 --- a/crates/compiler/fmt/src/spaces.rs +++ b/crates/compiler/fmt/src/spaces.rs @@ -1,7 +1,6 @@ -use bumpalo::Bump; -use roc_parse::{ast::CommentOrNewline, remove_spaces::RemoveSpaces}; +use roc_parse::ast::CommentOrNewline; -use crate::{Ast, Buf}; +use crate::Buf; /// The number of spaces to indent. pub const INDENT: u16 = 4; @@ -192,12 +191,3 @@ fn fmt_docs(buf: &mut Buf, docs: &str) { } buf.push_str(docs.trim_end()); } - -impl<'a> RemoveSpaces<'a> for Ast<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - Ast { - module: self.module.remove_spaces(arena), - defs: self.defs.remove_spaces(arena), - } - } -} diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 54963a2c060..96fd1aa33c8 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -16,7 +16,7 @@ mod test_reporting { use roc_load::{self, ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading}; use roc_module::symbol::{Interns, ModuleId}; use roc_packaging::cache::RocCacheDir; - use roc_parse::module::parse_header; + use roc_parse::header::parse_header; use roc_parse::state::State; use roc_parse::test_helpers::parse_expr_with; use roc_problem::Severity; @@ -359,7 +359,7 @@ mod test_reporting { let src_lines: Vec<&str> = src.split('\n').collect(); let lines = LineInfo::new(src); - match roc_parse::module::parse_header(arena, state) { + match roc_parse::header::parse_header(arena, state) { Err(fail) => { let interns = Interns::default(); let home = crate::helpers::test_home(); @@ -10875,12 +10875,12 @@ All branches in an `if` must have the same type! ), @r#" ── EMPTY RECORD BUILDER in /code/proj/Main.roc ───────────────────────────────── - + This record builder has no fields: - + 4│ { a <- } ^^^^^^^^ - + I need at least two fields to combine their values into a record. "# ); @@ -10898,11 +10898,11 @@ All branches in an `if` must have the same type! ── NOT ENOUGH FIELDS IN RECORD BUILDER in /code/proj/Main.roc ────────────────── This record builder only has one field: - + 4│> { a <- 5│> b: 123 6│> } - + I need at least two fields to combine their values into a record. "# ); @@ -10919,14 +10919,14 @@ All branches in an `if` must have the same type! ), @r#" ── OPTIONAL FIELD IN RECORD BUILDER in /code/proj/Main.roc ───────────────────── - + Optional fields are not allowed to be used in record builders. - + 4│ { a <- 5│ b: 123, 6│> c? 456 7│ } - + Record builders can only have required values for their fields. "# ); diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index 0676184de53..7d9bdfab02b 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -48,11 +48,11 @@ use roc_mono::reset_reuse; use roc_mono::{drop_specialization, inc_dec}; use roc_packaging::cache::RocCacheDir; use roc_parse::ast::{self, CommentOrNewline, ExtractSpaces, Spaced, ValueDef}; +use roc_parse::header::parse_module_defs; use roc_parse::header::{ self, AppHeader, ExposedName, HeaderType, ImportsKeywordItem, PackageEntry, PackageHeader, PlatformHeader, To, TypedIdent, }; -use roc_parse::module::parse_module_defs; use roc_parse::parser::{FileError, SourceError, SyntaxError}; use roc_problem::Severity; use roc_region::all::{LineInfo, Loc, Region}; @@ -1327,8 +1327,8 @@ fn load_packages_from_main<'a>( let parse_state = roc_parse::state::State::new(arena.alloc(src_bytes)); - let (parsed_module, _) = - roc_parse::module::parse_header(arena, parse_state.clone()).map_err(|fail| { + let (parsed_header, _) = + roc_parse::header::parse_header(arena, parse_state.clone()).map_err(|fail| { LoadingProblem::ParsingFailed( fail.map_problem(SyntaxError::Header) .into_file_error(filename.clone()), @@ -1337,7 +1337,7 @@ fn load_packages_from_main<'a>( use ast::Header::*; - let packages = match parsed_module.header { + let packages = match parsed_header.item { App(AppHeader { packages, .. }) | Package(PackageHeader { packages, .. }) => { unspace(arena, packages.value.items) } @@ -3349,7 +3349,7 @@ fn load_package_from_disk<'a>( let parse_start = Instant::now(); let bytes = arena.alloc(bytes_vec); let parse_state = roc_parse::state::State::new(bytes); - let parsed = roc_parse::module::parse_header(arena, parse_state.clone()); + let parsed = roc_parse::header::parse_header(arena, parse_state.clone()); let parse_header_duration = parse_start.elapsed(); // Insert the first entries for this module's timings @@ -3360,8 +3360,8 @@ fn load_package_from_disk<'a>( match parsed { Ok(( - ast::Module { - header: ast::Header::Module(header), + ast::SpacesBefore { + item: ast::Header::Module(header), .. }, _parse_state, @@ -3369,8 +3369,8 @@ fn load_package_from_disk<'a>( "expected platform/package module, got Module with header\n{header:?}" ))), Ok(( - ast::Module { - header: ast::Header::Hosted(header), + ast::SpacesBefore { + item: ast::Header::Hosted(header), .. }, _parse_state, @@ -3378,8 +3378,8 @@ fn load_package_from_disk<'a>( "expected platform/package module, got Hosted module with header\n{header:?}" ))), Ok(( - ast::Module { - header: ast::Header::App(header), + ast::SpacesBefore { + item: ast::Header::App(header), .. }, _parse_state, @@ -3387,9 +3387,9 @@ fn load_package_from_disk<'a>( "expected platform/package module, got App with header\n{header:?}" ))), Ok(( - ast::Module { - header: ast::Header::Package(header), - comments, + ast::SpacesBefore { + item: ast::Header::Package(header), + before: comments, }, parser_state, )) => { @@ -3430,9 +3430,9 @@ fn load_package_from_disk<'a>( Ok(Msg::Many(messages)) } Ok(( - ast::Module { - header: ast::Header::Platform(header), - comments, + ast::SpacesBefore { + item: ast::Header::Platform(header), + before: comments, }, parser_state, )) => { @@ -3530,13 +3530,13 @@ fn load_builtin_module_help<'a>( let opt_shorthand = None; let filename = PathBuf::from(filename); let parse_state = roc_parse::state::State::new(src_bytes.as_bytes()); - let parsed = roc_parse::module::parse_header(arena, parse_state.clone()); + let parsed = roc_parse::header::parse_header(arena, parse_state.clone()); match parsed { Ok(( - ast::Module { - header: ast::Header::Module(header), - comments, + ast::SpacesBefore { + item: ast::Header::Module(header), + before: comments, }, parse_state, )) => { @@ -3786,7 +3786,7 @@ fn parse_header<'a>( ) -> Result, LoadingProblem<'a>> { let parse_start = Instant::now(); let parse_state = roc_parse::state::State::new(src_bytes); - let parsed = roc_parse::module::parse_header(arena, parse_state.clone()); + let parsed = roc_parse::header::parse_header(arena, parse_state.clone()); let parse_header_duration = parse_start.elapsed(); if let Err(problem) = ensure_roc_file(&filename, src_bytes) { @@ -3815,9 +3815,9 @@ fn parse_header<'a>( match parsed { Ok(( - ast::Module { - header: ast::Header::Module(header), - comments, + ast::SpacesBefore { + item: ast::Header::Module(header), + before: comments, }, parse_state, )) => { @@ -3852,9 +3852,9 @@ fn parse_header<'a>( }) } Ok(( - ast::Module { - header: ast::Header::Hosted(header), - comments, + ast::SpacesBefore { + item: ast::Header::Hosted(header), + before: comments, }, parse_state, )) => { @@ -3883,9 +3883,9 @@ fn parse_header<'a>( }) } Ok(( - ast::Module { - header: ast::Header::App(header), - comments, + ast::SpacesBefore { + item: ast::Header::App(header), + before: comments, }, parse_state, )) => { @@ -3988,9 +3988,9 @@ fn parse_header<'a>( }) } Ok(( - ast::Module { - header: ast::Header::Package(header), - comments, + ast::SpacesBefore { + item: ast::Header::Package(header), + before: comments, }, parse_state, )) => { @@ -4015,9 +4015,9 @@ fn parse_header<'a>( } Ok(( - ast::Module { - header: ast::Header::Platform(header), - comments, + ast::SpacesBefore { + item: ast::Header::Platform(header), + before: comments, }, parse_state, )) => { @@ -5153,7 +5153,7 @@ fn parse<'a>( let parse_state = header.parse_state; let header_import_defs = - roc_parse::ast::Module::header_imports_to_defs(arena, header.header_imports); + roc_parse::ast::Header::header_imports_to_defs(arena, header.header_imports); let parsed_defs = match parse_module_defs(arena, parse_state.clone(), header_import_defs) { Ok(success) => success, diff --git a/crates/compiler/parse/benches/bench_parse.rs b/crates/compiler/parse/benches/bench_parse.rs index ecdf751deba..d3b4b1eee48 100644 --- a/crates/compiler/parse/benches/bench_parse.rs +++ b/crates/compiler/parse/benches/bench_parse.rs @@ -2,7 +2,7 @@ use bumpalo::Bump; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use roc_parse::{ ast::Defs, - module::{self, parse_module_defs}, + header::{self, parse_module_defs}, state::State, }; use std::path::PathBuf; @@ -20,7 +20,7 @@ pub fn parse_benchmark(c: &mut Criterion) { let arena = Bump::new(); let (_actual, state) = - module::parse_header(&arena, State::new(src.as_bytes())).unwrap(); + header::parse_header(&arena, State::new(src.as_bytes())).unwrap(); let res = parse_module_defs(&arena, state, Defs::default()).unwrap(); @@ -41,7 +41,7 @@ pub fn parse_benchmark(c: &mut Criterion) { let arena = Bump::new(); let (_actual, state) = - module::parse_header(&arena, State::new(src.as_bytes())).unwrap(); + header::parse_header(&arena, State::new(src.as_bytes())).unwrap(); let res = parse_module_defs(&arena, state, Defs::default()).unwrap(); diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 5844e18c7bc..e9effe63b32 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -14,6 +14,12 @@ use roc_module::called_via::{BinOp, CalledVia, UnaryOp}; use roc_module::ident::QualifiedModuleName; use roc_region::all::{Loc, Position, Region}; +#[derive(Debug, Clone)] +pub struct Full<'a> { + pub header: SpacesBefore<'a, Header<'a>>, + pub defs: Defs<'a>, +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Spaces<'a, T> { pub before: &'a [CommentOrNewline<'a>], @@ -111,15 +117,9 @@ impl<'a, T: ExtractSpaces<'a>> ExtractSpaces<'a> for Loc { } } -#[derive(Clone, Debug, PartialEq)] -pub struct Module<'a> { - pub comments: &'a [CommentOrNewline<'a>], - pub header: Header<'a>, -} - -impl<'a> Module<'a> { +impl<'a> Header<'a> { pub fn upgrade_header_imports(self, arena: &'a Bump) -> (Self, Defs<'a>) { - let (header, defs) = match self.header { + let (header, defs) = match self { Header::Module(header) => ( Header::Module(ModuleHeader { interface_imports: None, @@ -134,12 +134,10 @@ impl<'a> Module<'a> { }), Self::header_imports_to_defs(arena, header.old_imports), ), - Header::Package(_) | Header::Platform(_) | Header::Hosted(_) => { - (self.header, Defs::default()) - } + Header::Package(_) | Header::Platform(_) | Header::Hosted(_) => (self, Defs::default()), }; - (Module { header, ..self }, defs) + (header, defs) } pub fn header_imports_to_defs( @@ -2438,9 +2436,9 @@ pub trait Malformed { fn is_malformed(&self) -> bool; } -impl<'a> Malformed for Module<'a> { +impl<'a> Malformed for Full<'a> { fn is_malformed(&self) -> bool { - self.header.is_malformed() + self.header.item.is_malformed() || self.defs.is_malformed() } } @@ -2462,6 +2460,12 @@ impl<'a, T: Malformed> Malformed for Spaces<'a, T> { } } +impl<'a, T: Malformed> Malformed for SpacesBefore<'a, T> { + fn is_malformed(&self) -> bool { + self.item.is_malformed() + } +} + impl<'a> Malformed for Expr<'a> { fn is_malformed(&self) -> bool { use Expr::*; diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 1e5036c4981..517807efcef 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -9,10 +9,10 @@ use crate::blankspace::{ loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before, }; +use crate::header::module_name_help; use crate::ident::{ integer_ident, lowercase_ident, parse_ident, unqualified_ident, Accessor, Ident, Suffix, }; -use crate::module::module_name_help; use crate::parser::{ self, and, backtrackable, between, byte, byte_indent, collection_inner, collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map, @@ -24,8 +24,8 @@ use crate::parser::{ use crate::pattern::closure_param; use crate::state::State; use crate::string_literal::{self, StrLikeLiteral}; +use crate::type_annotation; use crate::{header, keyword}; -use crate::{module, type_annotation}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_collections::soa::Slice; @@ -948,7 +948,7 @@ fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport fn import_as<'a>( ) -> impl Parser<'a, header::KeywordItem<'a, ImportAsKeyword, Loc>>, EImport<'a>> { record!(header::KeywordItem { - keyword: module::spaces_around_keyword( + keyword: header::spaces_around_keyword( ImportAsKeyword, EImport::As, EImport::IndentAs, @@ -982,7 +982,7 @@ fn import_exposing<'a>() -> impl Parser< EImport<'a>, > { record!(header::KeywordItem { - keyword: module::spaces_around_keyword( + keyword: header::spaces_around_keyword( ImportExposingKeyword, EImport::Exposing, EImport::IndentExposing, @@ -1027,7 +1027,7 @@ fn import_ingested_file_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> fn import_ingested_file_as<'a>( ) -> impl Parser<'a, header::KeywordItem<'a, ImportAsKeyword, Loc<&'a str>>, EImport<'a>> { record!(header::KeywordItem { - keyword: module::spaces_around_keyword( + keyword: header::spaces_around_keyword( ImportAsKeyword, EImport::As, EImport::IndentAs, diff --git a/crates/compiler/parse/src/header.rs b/crates/compiler/parse/src/header.rs index 6bbda2cea7e..bc3342e9e63 100644 --- a/crates/compiler/parse/src/header.rs +++ b/crates/compiler/parse/src/header.rs @@ -1,18 +1,948 @@ +use std::fmt::Debug; + use crate::ast::{ - Collection, CommentOrNewline, Malformed, Pattern, Spaced, Spaces, StrLiteral, TypeAnnotation, + Collection, CommentOrNewline, Defs, Header, Malformed, Pattern, Spaced, Spaces, SpacesBefore, + StrLiteral, TypeAnnotation, }; -use crate::blankspace::space0_e; +use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::expr::merge_spaces; -use crate::ident::{lowercase_ident, UppercaseIdent}; +use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent}; +use crate::parser::Progress::{self, *}; use crate::parser::{ - and, byte, loc, map_with_arena, skip_first, skip_second, specialize_err, EPackageEntry, - EPackageName, Parser, + and, backtrackable, byte, collection_trailing_sep_e, increment_min_indent, loc, map, + map_with_arena, optional, reset_min_indent, skip_first, skip_second, specialize_err, succeed, + then, two_bytes, zero_or_more, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, + EPackageEntry, EPackageName, EPackages, EParams, EProvides, ERequires, ETypedIdent, Parser, + SourceError, SpaceProblem, SyntaxError, }; -use crate::parser::{optional, then}; -use crate::string_literal; +use crate::pattern::record_pattern_fields; +use crate::state::State; +use crate::string_literal::{self, parse_str_literal}; +use crate::type_annotation; use roc_module::symbol::{ModuleId, Symbol}; -use roc_region::all::Loc; -use std::fmt::Debug; +use roc_region::all::{Loc, Position, Region}; + +fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { + |_arena, state: State<'a>, _min_indent: u32| { + if state.has_reached_end() { + Ok((NoProgress, (), state)) + } else { + Err((NoProgress, SyntaxError::NotEndOfFile(state.pos()))) + } + } +} + +pub fn parse_module_defs<'a>( + arena: &'a bumpalo::Bump, + state: State<'a>, + defs: Defs<'a>, +) -> Result, SyntaxError<'a>> { + let min_indent = 0; + match crate::expr::parse_top_level_defs(arena, state.clone(), defs) { + Ok((_, defs, state)) => match end_of_file().parse(arena, state, min_indent) { + Ok(_) => Ok(defs), + Err((_, fail)) => Err(fail), + }, + Err((_, fail)) => Err(SyntaxError::Expr(fail, state.pos())), + } +} + +pub fn parse_header<'a>( + arena: &'a bumpalo::Bump, + state: State<'a>, +) -> Result<(SpacesBefore<'a, Header<'a>>, State<'a>), SourceError<'a, EHeader<'a>>> { + let min_indent = 0; + match header().parse(arena, state.clone(), min_indent) { + Ok((_, module, state)) => Ok((module, state)), + Err((_, fail)) => Err(SourceError::new(fail, &state)), + } +} + +pub fn header<'a>() -> impl Parser<'a, SpacesBefore<'a, Header<'a>>, EHeader<'a>> { + use crate::parser::keyword; + + record!(SpacesBefore { + before: space0_e(EHeader::IndentStart), + item: one_of![ + map( + skip_first( + keyword("module", EHeader::Start), + increment_min_indent(module_header()) + ), + Header::Module + ), + map( + skip_first( + keyword("interface", EHeader::Start), + increment_min_indent(interface_header()) + ), + Header::Module + ), + map( + skip_first( + keyword("app", EHeader::Start), + increment_min_indent(one_of![app_header(), old_app_header()]) + ), + Header::App + ), + map( + skip_first( + keyword("package", EHeader::Start), + increment_min_indent(one_of![package_header(), old_package_header()]) + ), + Header::Package + ), + map( + skip_first( + keyword("platform", EHeader::Start), + increment_min_indent(platform_header()) + ), + Header::Platform + ), + map( + skip_first( + keyword("hosted", EHeader::Start), + increment_min_indent(hosted_header()) + ), + Header::Hosted + ), + ] + }) +} + +#[inline(always)] +fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> { + record!(ModuleHeader { + after_keyword: space0_e(EHeader::IndentStart), + params: optional(specialize_err(EHeader::Params, module_params())), + exposes: specialize_err(EHeader::Exposes, exposes_list()), + interface_imports: succeed(None) + }) + .trace("module_header") +} + +fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> { + record!(ModuleParams { + params: specialize_err(EParams::Pattern, record_pattern_fields()), + before_arrow: skip_second( + space0_e(EParams::BeforeArrow), + loc(two_bytes(b'-', b'>', EParams::Arrow)) + ), + after_arrow: space0_e(EParams::AfterArrow), + }) +} + +// TODO does this need to be a macro? +macro_rules! merge_n_spaces { + ($arena:expr, $($slice:expr),*) => { + { + let mut merged = bumpalo::collections::Vec::with_capacity_in(0 $(+ $slice.len())*, $arena); + $(merged.extend_from_slice($slice);)* + merged.into_bump_slice() + } + }; +} + +/// Parse old interface headers so we can format them into module headers +#[inline(always)] +fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> { + let after_keyword = map_with_arena( + and( + skip_second( + space0_e(EHeader::IndentStart), + loc(module_name_help(EHeader::ModuleName)), + ), + specialize_err(EHeader::Exposes, exposes_kw()), + ), + |arena: &'a bumpalo::Bump, + (before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| { + merge_n_spaces!(arena, before_name, kw.before, kw.after) + }, + ); + + record!(ModuleHeader { + after_keyword: after_keyword, + params: succeed(None), + exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"), + interface_imports: map( + specialize_err(EHeader::Imports, imports()), + imports_none_if_empty + ) + .trace("imports"), + }) + .trace("interface_header") +} + +fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option> { + if value.item.is_empty() { + None + } else { + Some(value) + } +} + +#[inline(always)] +fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> { + record!(HostedHeader { + before_name: space0_e(EHeader::IndentStart), + name: loc(module_name_help(EHeader::ModuleName)), + exposes: specialize_err(EHeader::Exposes, exposes_values_kw()), + imports: specialize_err(EHeader::Imports, imports()), + generates: specialize_err(EHeader::Generates, generates()), + generates_with: specialize_err(EHeader::GeneratesWith, generates_with()), + }) + .trace("hosted_header") +} + +fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> { + use encode_unicode::CharExt; + + let mut chomped = 0; + + if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if first_letter.is_uppercase() { + chomped += width; + } else { + return Err(Progress::NoProgress); + } + } + + while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + // After the first character, only these are allowed: + // + // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers + // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() + // * A '.' separating module parts + if ch.is_alphabetic() || ch.is_ascii_digit() { + chomped += width; + } else if ch == '.' { + chomped += width; + + if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { + if first_letter.is_uppercase() { + chomped += width; + } else if first_letter == '{' { + // the .{ starting a `Foo.{ bar, baz }` importing clauses + chomped -= width; + break; + } else { + return Err(Progress::MadeProgress); + } + } + } else { + // we're done + break; + } + } + + let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; + + Ok(name) +} + +#[inline(always)] +fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> { + |_, mut state: State<'a>, _min_indent: u32| match chomp_module_name(state.bytes()) { + Ok(name) => { + let width = name.len(); + state = state.advance(width); + + Ok((MadeProgress, ModuleName::new(name), state)) + } + Err(progress) => Err((progress, ())), + } +} + +#[inline(always)] +fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { + record!(AppHeader { + before_provides: space0_e(EHeader::IndentStart), + provides: specialize_err(EHeader::Exposes, exposes_list()), + before_packages: space0_e(EHeader::IndentStart), + packages: specialize_err(EHeader::Packages, loc(packages_collection())), + old_imports: succeed(None), + old_provides_to_new_package: succeed(None), + }) + .trace("app_header") +} + +struct OldAppHeader<'a> { + pub before_name: &'a [CommentOrNewline<'a>], + pub packages: Option>>, + pub imports: Option>>, + pub provides: ProvidesTo<'a>, +} + +type OldAppPackages<'a> = + KeywordItem<'a, PackagesKeyword, Collection<'a, Loc>>>>; + +#[inline(always)] +fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { + let old = record!(OldAppHeader { + before_name: skip_second( + space0_e(EHeader::IndentStart), + loc(crate::parser::specialize_err( + EHeader::AppName, + string_literal::parse_str_literal() + )) + ), + packages: optional(specialize_err(EHeader::Packages, loc(packages()))), + imports: optional(specialize_err(EHeader::Imports, imports())), + provides: specialize_err(EHeader::Provides, provides_to()), + }); + + map_with_arena(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| { + let mut before_packages: &'a [CommentOrNewline] = &[]; + + let packages = match old.packages { + Some(packages) => { + before_packages = merge_spaces( + arena, + packages.value.keyword.before, + packages.value.keyword.after, + ); + + if let To::ExistingPackage(platform_shorthand) = old.provides.to.value { + packages.map(|coll| { + coll.item.map_items(arena, |loc_spaced_pkg| { + if loc_spaced_pkg.value.item().shorthand == platform_shorthand { + loc_spaced_pkg.map(|spaced_pkg| { + spaced_pkg.map(arena, |pkg| { + let mut new_pkg = *pkg; + new_pkg.platform_marker = Some(merge_spaces( + arena, + old.provides.to_keyword.before, + old.provides.to_keyword.after, + )); + new_pkg + }) + }) + } else { + *loc_spaced_pkg + } + }) + }) + } else { + packages.map(|kw| kw.item) + } + } + None => Loc { + region: Region::zero(), + value: Collection::empty(), + }, + }; + + let provides = match old.provides.types { + Some(types) => { + let mut combined_items = bumpalo::collections::Vec::with_capacity_in( + old.provides.entries.items.len() + types.items.len(), + arena, + ); + + combined_items.extend_from_slice(old.provides.entries.items); + + for loc_spaced_type_ident in types.items { + combined_items.push(loc_spaced_type_ident.map(|spaced_type_ident| { + spaced_type_ident.map(arena, |type_ident| { + ExposedName::new(From::from(*type_ident)) + }) + })); + } + + let value_comments = old.provides.entries.final_comments(); + let type_comments = types.final_comments(); + + let mut combined_comments = bumpalo::collections::Vec::with_capacity_in( + value_comments.len() + type_comments.len(), + arena, + ); + combined_comments.extend_from_slice(value_comments); + combined_comments.extend_from_slice(type_comments); + + Collection::with_items_and_comments( + arena, + combined_items.into_bump_slice(), + combined_comments.into_bump_slice(), + ) + } + None => old.provides.entries, + }; + + AppHeader { + before_provides: merge_spaces( + arena, + old.before_name, + old.provides.provides_keyword.before, + ), + provides, + before_packages: merge_spaces( + arena, + before_packages, + old.provides.provides_keyword.after, + ), + packages, + old_imports: old.imports.and_then(imports_none_if_empty), + old_provides_to_new_package: match old.provides.to.value { + To::NewPackage(new_pkg) => Some(new_pkg), + To::ExistingPackage(_) => None, + }, + } + }) +} + +#[inline(always)] +fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> { + record!(PackageHeader { + before_exposes: space0_e(EHeader::IndentStart), + exposes: specialize_err(EHeader::Exposes, exposes_module_collection()), + before_packages: space0_e(EHeader::IndentStart), + packages: specialize_err(EHeader::Packages, loc(packages_collection())), + }) + .trace("package_header") +} + +#[derive(Debug, Clone, PartialEq)] +struct OldPackageHeader<'a> { + before_name: &'a [CommentOrNewline<'a>], + exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc>>>>, + packages: + Loc>>>>>, +} + +#[inline(always)] +fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> { + map_with_arena( + record!(OldPackageHeader { + before_name: skip_second( + space0_e(EHeader::IndentStart), + specialize_err(EHeader::PackageName, package_name()) + ), + exposes: specialize_err(EHeader::Exposes, exposes_modules()), + packages: specialize_err(EHeader::Packages, loc(packages())), + }), + |arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| { + let before_exposes = merge_n_spaces!( + arena, + old.before_name, + old.exposes.keyword.before, + old.exposes.keyword.after + ); + let before_packages = merge_spaces( + arena, + old.packages.value.keyword.before, + old.packages.value.keyword.after, + ); + + PackageHeader { + before_exposes, + exposes: old.exposes.item, + before_packages, + packages: old.packages.map(|kw| kw.item), + } + }, + ) + .trace("old_package_header") +} + +#[inline(always)] +fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { + record!(PlatformHeader { + before_name: space0_e(EHeader::IndentStart), + name: loc(specialize_err(EHeader::PlatformName, package_name())), + requires: specialize_err(EHeader::Requires, requires()), + exposes: specialize_err(EHeader::Exposes, exposes_modules()), + packages: specialize_err(EHeader::Packages, packages()), + imports: specialize_err(EHeader::Imports, imports()), + provides: specialize_err(EHeader::Provides, provides_exposed()), + }) + .trace("platform_header") +} + +fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> { + one_of![ + specialize_err( + |_, pos| EProvides::Identifier(pos), + map(lowercase_ident(), To::ExistingPackage) + ), + specialize_err(EProvides::Package, map(package_name(), To::NewPackage)) + ] +} + +#[inline(always)] +fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> { + record!(ProvidesTo { + provides_keyword: spaces_around_keyword( + ProvidesKeyword, + EProvides::Provides, + EProvides::IndentProvides, + EProvides::IndentListStart + ), + entries: collection_trailing_sep_e( + byte(b'[', EProvides::ListStart), + exposes_entry(EProvides::Identifier), + byte(b',', EProvides::ListEnd), + byte(b']', EProvides::ListEnd), + Spaced::SpaceBefore + ), + types: optional(backtrackable(provides_types())), + to_keyword: spaces_around_keyword( + ToKeyword, + EProvides::To, + EProvides::IndentTo, + EProvides::IndentListStart + ), + to: loc(provides_to_package()), + }) + .trace("provides_to") +} + +fn provides_exposed<'a>() -> impl Parser< + 'a, + KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc>>>>, + EProvides<'a>, +> { + record!(KeywordItem { + keyword: spaces_around_keyword( + ProvidesKeyword, + EProvides::Provides, + EProvides::IndentProvides, + EProvides::IndentListStart + ), + item: collection_trailing_sep_e( + byte(b'[', EProvides::ListStart), + exposes_entry(EProvides::Identifier), + byte(b',', EProvides::ListEnd), + byte(b']', EProvides::ListEnd), + Spaced::SpaceBefore + ), + }) +} + +#[inline(always)] +fn provides_types<'a>( +) -> impl Parser<'a, Collection<'a, Loc>>>, EProvides<'a>> { + skip_first( + // We only support spaces here, not newlines, because this is not intended + // to be the design forever. Someday it will hopefully work like Elm, + // where platform authors can provide functions like Browser.sandbox which + // present an API based on ordinary-looking type variables. + zero_or_more(byte( + b' ', + // HACK: If this errors, EProvides::Provides is not an accurate reflection + // of what went wrong. However, this is both skipped and zero_or_more, + // so this error should never be visible to anyone in practice! + EProvides::Provides, + )), + collection_trailing_sep_e( + byte(b'{', EProvides::ListStart), + provides_type_entry(EProvides::Identifier), + byte(b',', EProvides::ListEnd), + byte(b'}', EProvides::ListEnd), + Spaced::SpaceBefore, + ), + ) +} + +fn provides_type_entry<'a, F, E>( + to_expectation: F, +) -> impl Parser<'a, Loc>>, E> +where + F: Fn(Position) -> E, + F: Copy, + E: 'a, +{ + loc(map( + specialize_err(move |_, pos| to_expectation(pos), ident::uppercase()), + Spaced::Item, + )) +} + +fn exposes_entry<'a, F, E>( + to_expectation: F, +) -> impl Parser<'a, Loc>>, E> +where + F: Fn(Position) -> E, + F: Copy, + E: 'a, +{ + loc(map( + specialize_err(move |_, pos| to_expectation(pos), unqualified_ident()), + |n| Spaced::Item(ExposedName::new(n)), + )) +} + +#[inline(always)] +fn requires<'a>( +) -> impl Parser<'a, KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>, ERequires<'a>> { + record!(KeywordItem { + keyword: spaces_around_keyword( + RequiresKeyword, + ERequires::Requires, + ERequires::IndentRequires, + ERequires::IndentListStart + ), + item: platform_requires(), + }) +} + +#[inline(always)] +fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> { + record!(PlatformRequires { + rigids: skip_second(requires_rigids(), space0_e(ERequires::ListStart)), + signature: requires_typed_ident() + }) +} + +#[inline(always)] +fn requires_rigids<'a>( +) -> impl Parser<'a, Collection<'a, Loc>>>, ERequires<'a>> { + collection_trailing_sep_e( + byte(b'{', ERequires::ListStart), + specialize_err( + |_, pos| ERequires::Rigid(pos), + loc(map(ident::uppercase(), Spaced::Item)), + ), + byte(b',', ERequires::ListEnd), + byte(b'}', ERequires::ListEnd), + Spaced::SpaceBefore, + ) +} + +#[inline(always)] +fn requires_typed_ident<'a>() -> impl Parser<'a, Loc>>, ERequires<'a>> { + skip_first( + byte(b'{', ERequires::ListStart), + skip_second( + reset_min_indent(space0_around_ee( + specialize_err(ERequires::TypedIdent, loc(typed_ident())), + ERequires::ListStart, + ERequires::ListEnd, + )), + byte(b'}', ERequires::ListStart), + ), + ) +} + +#[inline(always)] +fn exposes_values_kw<'a>() -> impl Parser< + 'a, + KeywordItem<'a, ExposesKeyword, Collection<'a, Loc>>>>, + EExposes, +> { + record!(KeywordItem { + keyword: exposes_kw(), + item: exposes_list() + }) +} + +#[inline(always)] +fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> { + spaces_around_keyword( + ExposesKeyword, + EExposes::Exposes, + EExposes::IndentExposes, + EExposes::IndentListStart, + ) +} + +#[inline(always)] +fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc>>>, EExposes> +{ + collection_trailing_sep_e( + byte(b'[', EExposes::ListStart), + exposes_entry(EExposes::Identifier), + byte(b',', EExposes::ListEnd), + byte(b']', EExposes::ListEnd), + Spaced::SpaceBefore, + ) +} + +pub fn spaces_around_keyword<'a, K: Keyword, E>( + keyword_item: K, + expectation: fn(Position) -> E, + indent_problem1: fn(Position) -> E, + indent_problem2: fn(Position) -> E, +) -> impl Parser<'a, Spaces<'a, K>, E> +where + E: 'a + SpaceProblem, +{ + map( + and( + skip_second( + // parse any leading space before the keyword + backtrackable(space0_e(indent_problem1)), + // parse the keyword + crate::parser::keyword(K::KEYWORD, expectation), + ), + // parse the trailing space + space0_e(indent_problem2), + ), + move |(before, after)| Spaces { + before, + item: keyword_item, + after, + }, + ) +} + +#[inline(always)] +fn exposes_modules<'a>() -> impl Parser< + 'a, + KeywordItem<'a, ExposesKeyword, Collection<'a, Loc>>>>, + EExposes, +> { + record!(KeywordItem { + keyword: spaces_around_keyword( + ExposesKeyword, + EExposes::Exposes, + EExposes::IndentExposes, + EExposes::IndentListStart + ), + item: exposes_module_collection(), + }) +} + +fn exposes_module_collection<'a>( +) -> impl Parser<'a, Collection<'a, Loc>>>, EExposes> { + collection_trailing_sep_e( + byte(b'[', EExposes::ListStart), + exposes_module(EExposes::Identifier), + byte(b',', EExposes::ListEnd), + byte(b']', EExposes::ListEnd), + Spaced::SpaceBefore, + ) +} + +fn exposes_module<'a, F, E>( + to_expectation: F, +) -> impl Parser<'a, Loc>>, E> +where + F: Fn(Position) -> E, + F: Copy, + E: 'a, +{ + loc(map( + specialize_err(move |_, pos| to_expectation(pos), module_name()), + Spaced::Item, + )) +} + +#[inline(always)] +fn packages<'a>() -> impl Parser< + 'a, + KeywordItem<'a, PackagesKeyword, Collection<'a, Loc>>>>, + EPackages<'a>, +> { + record!(KeywordItem { + keyword: packages_kw(), + item: packages_collection() + }) +} + +#[inline(always)] +fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'a>> { + spaces_around_keyword( + PackagesKeyword, + EPackages::Packages, + EPackages::IndentPackages, + EPackages::IndentListStart, + ) +} + +#[inline(always)] +fn packages_collection<'a>( +) -> impl Parser<'a, Collection<'a, Loc>>>, EPackages<'a>> { + collection_trailing_sep_e( + byte(b'{', EPackages::ListStart), + specialize_err(EPackages::PackageEntry, loc(package_entry())), + byte(b',', EPackages::ListEnd), + byte(b'}', EPackages::ListEnd), + Spaced::SpaceBefore, + ) +} + +#[inline(always)] +fn generates<'a>( +) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> { + record!(KeywordItem { + keyword: spaces_around_keyword( + GeneratesKeyword, + EGenerates::Generates, + EGenerates::IndentGenerates, + EGenerates::IndentTypeStart + ), + item: specialize_err(|(), pos| EGenerates::Identifier(pos), uppercase()) + }) +} + +#[inline(always)] +fn generates_with<'a>() -> impl Parser< + 'a, + KeywordItem<'a, WithKeyword, Collection<'a, Loc>>>>, + EGeneratesWith, +> { + record!(KeywordItem { + keyword: spaces_around_keyword( + WithKeyword, + EGeneratesWith::With, + EGeneratesWith::IndentWith, + EGeneratesWith::IndentListStart + ), + item: collection_trailing_sep_e( + byte(b'[', EGeneratesWith::ListStart), + exposes_entry(EGeneratesWith::Identifier), + byte(b',', EGeneratesWith::ListEnd), + byte(b']', EGeneratesWith::ListEnd), + Spaced::SpaceBefore + ) + }) +} + +#[inline(always)] +fn imports<'a>() -> impl Parser< + 'a, + KeywordItem<'a, ImportsKeyword, Collection<'a, Loc>>>>, + EImports, +> { + record!(KeywordItem { + keyword: spaces_around_keyword( + ImportsKeyword, + EImports::Imports, + EImports::IndentImports, + EImports::IndentListStart + ), + item: collection_trailing_sep_e( + byte(b'[', EImports::ListStart), + loc(imports_entry()), + byte(b',', EImports::ListEnd), + byte(b']', EImports::ListEnd), + Spaced::SpaceBefore + ) + }) + .trace("imports") +} + +#[inline(always)] +pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> { + // e.g. + // + // printLine : Str -> Effect {} + map( + and( + and( + loc(specialize_err( + |_, pos| ETypedIdent::Identifier(pos), + lowercase_ident(), + )), + space0_e(ETypedIdent::IndentHasType), + ), + skip_first( + byte(b':', ETypedIdent::HasType), + space0_before_e( + specialize_err( + ETypedIdent::Type, + reset_min_indent(type_annotation::located(true)), + ), + ETypedIdent::IndentType, + ), + ), + ), + |((ident, spaces_before_colon), ann)| { + Spaced::Item(TypedIdent { + ident, + spaces_before_colon, + ann, + }) + }, + ) +} + +fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> { + specialize_err(|_, pos| EImports::Shorthand(pos), lowercase_ident()) +} + +pub fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E> +where + F: Fn(Position) -> E, + E: 'a, + F: 'a, +{ + specialize_err(move |_, pos| to_expectation(pos), module_name()) +} + +#[inline(always)] +fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> { + type Temp<'a> = ( + (Option<&'a str>, ModuleName<'a>), + Option>>>>, + ); + + let spaced_import = |((opt_shortname, module_name), opt_values): Temp<'a>| { + let exposed_values = opt_values.unwrap_or_else(Collection::empty); + + let entry = match opt_shortname { + Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values), + + None => ImportsEntry::Module(module_name, exposed_values), + }; + + Spaced::Item(entry) + }; + + one_of!( + map( + and( + and( + // e.g. `pf.` + optional(backtrackable(skip_second( + shortname(), + byte(b'.', EImports::ShorthandDot) + ))), + // e.g. `Task` + module_name_help(EImports::ModuleName) + ), + // e.g. `.{ Task, after}` + optional(skip_first( + byte(b'.', EImports::ExposingDot), + collection_trailing_sep_e( + byte(b'{', EImports::SetStart), + exposes_entry(EImports::Identifier), + byte(b',', EImports::SetEnd), + byte(b'}', EImports::SetEnd), + Spaced::SpaceBefore + ) + )) + ), + spaced_import + ) + .trace("normal_import"), + map( + and( + and( + // e.g. "filename" + // TODO: str literal allows for multiline strings. We probably don't want that for file names. + specialize_err(|_, pos| EImports::StrLiteral(pos), parse_str_literal()), + // e.g. as + and( + and( + space0_e(EImports::AsKeyword), + two_bytes(b'a', b's', EImports::AsKeyword) + ), + space0_e(EImports::AsKeyword) + ) + ), + // e.g. file : Str + specialize_err(|_, pos| EImports::TypedIdent(pos), typed_ident()) + ), + |((file_name, _), typed_ident)| { + // TODO: look at blacking block strings during parsing. + Spaced::Item(ImportsEntry::IngestedFile(file_name, typed_ident)) + } + ) + .trace("ingest_file_import") + ) + .trace("imports_entry") +} impl<'a> HeaderType<'a> { pub fn exposed_or_provided_values(&'a self) -> &'a [Loc>] { @@ -178,7 +1108,7 @@ impl<'a> ModuleName<'a> { ModuleName(name) } - pub const fn as_str(&'a self) -> &'a str { + pub const fn as_str(&self) -> &'a str { self.0 } diff --git a/crates/compiler/parse/src/highlight.rs b/crates/compiler/parse/src/highlight.rs index c94df152597..b5b5c5fff49 100644 --- a/crates/compiler/parse/src/highlight.rs +++ b/crates/compiler/parse/src/highlight.rs @@ -74,7 +74,7 @@ pub fn highlight(text: &str) -> Vec> { let header_keywords = HEADER_KEYWORDS.iter().copied().collect::>(); let body_keywords = KEYWORDS.iter().copied().collect::>(); - if let Ok((_prog, _, new_state)) = crate::module::header().parse(&arena, state.clone(), 0) { + if let Ok((_prog, _, new_state)) = crate::header::header().parse(&arena, state.clone(), 0) { let inner_state = State::new(text[..state.bytes().len() - new_state.bytes().len()].as_bytes()); highlight_inner(&arena, inner_state, &mut tokens, &header_keywords); diff --git a/crates/compiler/parse/src/lib.rs b/crates/compiler/parse/src/lib.rs index dc8a9a3aaa0..9651baf4030 100644 --- a/crates/compiler/parse/src/lib.rs +++ b/crates/compiler/parse/src/lib.rs @@ -13,7 +13,6 @@ pub mod header; pub mod highlight; pub mod ident; pub mod keyword; -pub mod module; pub mod number_literal; pub mod pattern; pub mod problems; diff --git a/crates/compiler/parse/src/module.rs b/crates/compiler/parse/src/module.rs deleted file mode 100644 index 3a895cdd2a0..00000000000 --- a/crates/compiler/parse/src/module.rs +++ /dev/null @@ -1,945 +0,0 @@ -use crate::ast::{Collection, CommentOrNewline, Defs, Header, Module, Spaced, Spaces}; -use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; -use crate::expr::merge_spaces; -use crate::header::{ - package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, - HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword, - KeywordItem, ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader, - PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, - RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword, -}; -use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent}; -use crate::parser::Progress::{self, *}; -use crate::parser::{ - and, backtrackable, byte, collection_trailing_sep_e, increment_min_indent, loc, map, - map_with_arena, optional, reset_min_indent, skip_first, skip_second, specialize_err, succeed, - two_bytes, zero_or_more, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, - EParams, EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError, -}; -use crate::pattern::record_pattern_fields; -use crate::state::State; -use crate::string_literal::{self, parse_str_literal}; -use crate::type_annotation; -use roc_region::all::{Loc, Position, Region}; - -fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> { - |_arena, state: State<'a>, _min_indent: u32| { - if state.has_reached_end() { - Ok((NoProgress, (), state)) - } else { - Err((NoProgress, SyntaxError::NotEndOfFile(state.pos()))) - } - } -} - -pub fn parse_module_defs<'a>( - arena: &'a bumpalo::Bump, - state: State<'a>, - defs: Defs<'a>, -) -> Result, SyntaxError<'a>> { - let min_indent = 0; - match crate::expr::parse_top_level_defs(arena, state.clone(), defs) { - Ok((_, defs, state)) => match end_of_file().parse(arena, state, min_indent) { - Ok(_) => Ok(defs), - Err((_, fail)) => Err(fail), - }, - Err((_, fail)) => Err(SyntaxError::Expr(fail, state.pos())), - } -} - -pub fn parse_header<'a>( - arena: &'a bumpalo::Bump, - state: State<'a>, -) -> Result<(Module<'a>, State<'a>), SourceError<'a, EHeader<'a>>> { - let min_indent = 0; - match header().parse(arena, state.clone(), min_indent) { - Ok((_, module, state)) => Ok((module, state)), - Err((_, fail)) => Err(SourceError::new(fail, &state)), - } -} - -pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> { - use crate::parser::keyword; - - record!(Module { - comments: space0_e(EHeader::IndentStart), - header: one_of![ - map( - skip_first( - keyword("module", EHeader::Start), - increment_min_indent(module_header()) - ), - Header::Module - ), - map( - skip_first( - keyword("interface", EHeader::Start), - increment_min_indent(interface_header()) - ), - Header::Module - ), - map( - skip_first( - keyword("app", EHeader::Start), - increment_min_indent(one_of![app_header(), old_app_header()]) - ), - Header::App - ), - map( - skip_first( - keyword("package", EHeader::Start), - increment_min_indent(one_of![package_header(), old_package_header()]) - ), - Header::Package - ), - map( - skip_first( - keyword("platform", EHeader::Start), - increment_min_indent(platform_header()) - ), - Header::Platform - ), - map( - skip_first( - keyword("hosted", EHeader::Start), - increment_min_indent(hosted_header()) - ), - Header::Hosted - ), - ] - }) -} - -#[inline(always)] -fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> { - record!(ModuleHeader { - after_keyword: space0_e(EHeader::IndentStart), - params: optional(specialize_err(EHeader::Params, module_params())), - exposes: specialize_err(EHeader::Exposes, exposes_list()), - interface_imports: succeed(None) - }) - .trace("module_header") -} - -fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> { - record!(ModuleParams { - params: specialize_err(EParams::Pattern, record_pattern_fields()), - before_arrow: skip_second( - space0_e(EParams::BeforeArrow), - loc(two_bytes(b'-', b'>', EParams::Arrow)) - ), - after_arrow: space0_e(EParams::AfterArrow), - }) -} - -// TODO does this need to be a macro? -macro_rules! merge_n_spaces { - ($arena:expr, $($slice:expr),*) => { - { - let mut merged = bumpalo::collections::Vec::with_capacity_in(0 $(+ $slice.len())*, $arena); - $(merged.extend_from_slice($slice);)* - merged.into_bump_slice() - } - }; -} - -/// Parse old interface headers so we can format them into module headers -#[inline(always)] -fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> { - let after_keyword = map_with_arena( - and( - skip_second( - space0_e(EHeader::IndentStart), - loc(module_name_help(EHeader::ModuleName)), - ), - specialize_err(EHeader::Exposes, exposes_kw()), - ), - |arena: &'a bumpalo::Bump, - (before_name, kw): (&'a [CommentOrNewline<'a>], Spaces<'a, ExposesKeyword>)| { - merge_n_spaces!(arena, before_name, kw.before, kw.after) - }, - ); - - record!(ModuleHeader { - after_keyword: after_keyword, - params: succeed(None), - exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"), - interface_imports: map( - specialize_err(EHeader::Imports, imports()), - imports_none_if_empty - ) - .trace("imports"), - }) - .trace("interface_header") -} - -fn imports_none_if_empty(value: ImportsKeywordItem<'_>) -> Option> { - if value.item.is_empty() { - None - } else { - Some(value) - } -} - -#[inline(always)] -fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> { - record!(HostedHeader { - before_name: space0_e(EHeader::IndentStart), - name: loc(module_name_help(EHeader::ModuleName)), - exposes: specialize_err(EHeader::Exposes, exposes_values_kw()), - imports: specialize_err(EHeader::Imports, imports()), - generates: specialize_err(EHeader::Generates, generates()), - generates_with: specialize_err(EHeader::GeneratesWith, generates_with()), - }) - .trace("hosted_header") -} - -fn chomp_module_name(buffer: &[u8]) -> Result<&str, Progress> { - use encode_unicode::CharExt; - - let mut chomped = 0; - - if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { - if first_letter.is_uppercase() { - chomped += width; - } else { - return Err(Progress::NoProgress); - } - } - - while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { - // After the first character, only these are allowed: - // - // * Unicode alphabetic chars - you might include `鹏` if that's clear to your readers - // * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric() - // * A '.' separating module parts - if ch.is_alphabetic() || ch.is_ascii_digit() { - chomped += width; - } else if ch == '.' { - chomped += width; - - if let Ok((first_letter, width)) = char::from_utf8_slice_start(&buffer[chomped..]) { - if first_letter.is_uppercase() { - chomped += width; - } else if first_letter == '{' { - // the .{ starting a `Foo.{ bar, baz }` importing clauses - chomped -= width; - break; - } else { - return Err(Progress::MadeProgress); - } - } - } else { - // we're done - break; - } - } - - let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) }; - - Ok(name) -} - -#[inline(always)] -fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, ()> { - |_, mut state: State<'a>, _min_indent: u32| match chomp_module_name(state.bytes()) { - Ok(name) => { - let width = name.len(); - state = state.advance(width); - - Ok((MadeProgress, ModuleName::new(name), state)) - } - Err(progress) => Err((progress, ())), - } -} - -#[inline(always)] -fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { - record!(AppHeader { - before_provides: space0_e(EHeader::IndentStart), - provides: specialize_err(EHeader::Exposes, exposes_list()), - before_packages: space0_e(EHeader::IndentStart), - packages: specialize_err(EHeader::Packages, loc(packages_collection())), - old_imports: succeed(None), - old_provides_to_new_package: succeed(None), - }) - .trace("app_header") -} - -struct OldAppHeader<'a> { - pub before_name: &'a [CommentOrNewline<'a>], - pub packages: Option>>, - pub imports: Option>>, - pub provides: ProvidesTo<'a>, -} - -type OldAppPackages<'a> = - KeywordItem<'a, PackagesKeyword, Collection<'a, Loc>>>>; - -#[inline(always)] -fn old_app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> { - let old = record!(OldAppHeader { - before_name: skip_second( - space0_e(EHeader::IndentStart), - loc(crate::parser::specialize_err( - EHeader::AppName, - string_literal::parse_str_literal() - )) - ), - packages: optional(specialize_err(EHeader::Packages, loc(packages()))), - imports: optional(specialize_err(EHeader::Imports, imports())), - provides: specialize_err(EHeader::Provides, provides_to()), - }); - - map_with_arena(old, |arena: &'a bumpalo::Bump, old: OldAppHeader<'a>| { - let mut before_packages: &'a [CommentOrNewline] = &[]; - - let packages = match old.packages { - Some(packages) => { - before_packages = merge_spaces( - arena, - packages.value.keyword.before, - packages.value.keyword.after, - ); - - if let To::ExistingPackage(platform_shorthand) = old.provides.to.value { - packages.map(|coll| { - coll.item.map_items(arena, |loc_spaced_pkg| { - if loc_spaced_pkg.value.item().shorthand == platform_shorthand { - loc_spaced_pkg.map(|spaced_pkg| { - spaced_pkg.map(arena, |pkg| { - let mut new_pkg = *pkg; - new_pkg.platform_marker = Some(merge_spaces( - arena, - old.provides.to_keyword.before, - old.provides.to_keyword.after, - )); - new_pkg - }) - }) - } else { - *loc_spaced_pkg - } - }) - }) - } else { - packages.map(|kw| kw.item) - } - } - None => Loc { - region: Region::zero(), - value: Collection::empty(), - }, - }; - - let provides = match old.provides.types { - Some(types) => { - let mut combined_items = bumpalo::collections::Vec::with_capacity_in( - old.provides.entries.items.len() + types.items.len(), - arena, - ); - - combined_items.extend_from_slice(old.provides.entries.items); - - for loc_spaced_type_ident in types.items { - combined_items.push(loc_spaced_type_ident.map(|spaced_type_ident| { - spaced_type_ident.map(arena, |type_ident| { - ExposedName::new(From::from(*type_ident)) - }) - })); - } - - let value_comments = old.provides.entries.final_comments(); - let type_comments = types.final_comments(); - - let mut combined_comments = bumpalo::collections::Vec::with_capacity_in( - value_comments.len() + type_comments.len(), - arena, - ); - combined_comments.extend_from_slice(value_comments); - combined_comments.extend_from_slice(type_comments); - - Collection::with_items_and_comments( - arena, - combined_items.into_bump_slice(), - combined_comments.into_bump_slice(), - ) - } - None => old.provides.entries, - }; - - AppHeader { - before_provides: merge_spaces( - arena, - old.before_name, - old.provides.provides_keyword.before, - ), - provides, - before_packages: merge_spaces( - arena, - before_packages, - old.provides.provides_keyword.after, - ), - packages, - old_imports: old.imports.and_then(imports_none_if_empty), - old_provides_to_new_package: match old.provides.to.value { - To::NewPackage(new_pkg) => Some(new_pkg), - To::ExistingPackage(_) => None, - }, - } - }) -} - -#[inline(always)] -fn package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> { - record!(PackageHeader { - before_exposes: space0_e(EHeader::IndentStart), - exposes: specialize_err(EHeader::Exposes, exposes_module_collection()), - before_packages: space0_e(EHeader::IndentStart), - packages: specialize_err(EHeader::Packages, loc(packages_collection())), - }) - .trace("package_header") -} - -#[derive(Debug, Clone, PartialEq)] -struct OldPackageHeader<'a> { - before_name: &'a [CommentOrNewline<'a>], - exposes: KeywordItem<'a, ExposesKeyword, Collection<'a, Loc>>>>, - packages: - Loc>>>>>, -} - -#[inline(always)] -fn old_package_header<'a>() -> impl Parser<'a, PackageHeader<'a>, EHeader<'a>> { - map_with_arena( - record!(OldPackageHeader { - before_name: skip_second( - space0_e(EHeader::IndentStart), - specialize_err(EHeader::PackageName, package_name()) - ), - exposes: specialize_err(EHeader::Exposes, exposes_modules()), - packages: specialize_err(EHeader::Packages, loc(packages())), - }), - |arena: &'a bumpalo::Bump, old: OldPackageHeader<'a>| { - let before_exposes = merge_n_spaces!( - arena, - old.before_name, - old.exposes.keyword.before, - old.exposes.keyword.after - ); - let before_packages = merge_spaces( - arena, - old.packages.value.keyword.before, - old.packages.value.keyword.after, - ); - - PackageHeader { - before_exposes, - exposes: old.exposes.item, - before_packages, - packages: old.packages.map(|kw| kw.item), - } - }, - ) - .trace("old_package_header") -} - -#[inline(always)] -fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> { - record!(PlatformHeader { - before_name: space0_e(EHeader::IndentStart), - name: loc(specialize_err(EHeader::PlatformName, package_name())), - requires: specialize_err(EHeader::Requires, requires()), - exposes: specialize_err(EHeader::Exposes, exposes_modules()), - packages: specialize_err(EHeader::Packages, packages()), - imports: specialize_err(EHeader::Imports, imports()), - provides: specialize_err(EHeader::Provides, provides_exposed()), - }) - .trace("platform_header") -} - -fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, EProvides<'a>> { - one_of![ - specialize_err( - |_, pos| EProvides::Identifier(pos), - map(lowercase_ident(), To::ExistingPackage) - ), - specialize_err(EProvides::Package, map(package_name(), To::NewPackage)) - ] -} - -#[inline(always)] -fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> { - record!(ProvidesTo { - provides_keyword: spaces_around_keyword( - ProvidesKeyword, - EProvides::Provides, - EProvides::IndentProvides, - EProvides::IndentListStart - ), - entries: collection_trailing_sep_e( - byte(b'[', EProvides::ListStart), - exposes_entry(EProvides::Identifier), - byte(b',', EProvides::ListEnd), - byte(b']', EProvides::ListEnd), - Spaced::SpaceBefore - ), - types: optional(backtrackable(provides_types())), - to_keyword: spaces_around_keyword( - ToKeyword, - EProvides::To, - EProvides::IndentTo, - EProvides::IndentListStart - ), - to: loc(provides_to_package()), - }) - .trace("provides_to") -} - -fn provides_exposed<'a>() -> impl Parser< - 'a, - KeywordItem<'a, ProvidesKeyword, Collection<'a, Loc>>>>, - EProvides<'a>, -> { - record!(KeywordItem { - keyword: spaces_around_keyword( - ProvidesKeyword, - EProvides::Provides, - EProvides::IndentProvides, - EProvides::IndentListStart - ), - item: collection_trailing_sep_e( - byte(b'[', EProvides::ListStart), - exposes_entry(EProvides::Identifier), - byte(b',', EProvides::ListEnd), - byte(b']', EProvides::ListEnd), - Spaced::SpaceBefore - ), - }) -} - -#[inline(always)] -fn provides_types<'a>( -) -> impl Parser<'a, Collection<'a, Loc>>>, EProvides<'a>> { - skip_first( - // We only support spaces here, not newlines, because this is not intended - // to be the design forever. Someday it will hopefully work like Elm, - // where platform authors can provide functions like Browser.sandbox which - // present an API based on ordinary-looking type variables. - zero_or_more(byte( - b' ', - // HACK: If this errors, EProvides::Provides is not an accurate reflection - // of what went wrong. However, this is both skipped and zero_or_more, - // so this error should never be visible to anyone in practice! - EProvides::Provides, - )), - collection_trailing_sep_e( - byte(b'{', EProvides::ListStart), - provides_type_entry(EProvides::Identifier), - byte(b',', EProvides::ListEnd), - byte(b'}', EProvides::ListEnd), - Spaced::SpaceBefore, - ), - ) -} - -fn provides_type_entry<'a, F, E>( - to_expectation: F, -) -> impl Parser<'a, Loc>>, E> -where - F: Fn(Position) -> E, - F: Copy, - E: 'a, -{ - loc(map( - specialize_err(move |_, pos| to_expectation(pos), ident::uppercase()), - Spaced::Item, - )) -} - -fn exposes_entry<'a, F, E>( - to_expectation: F, -) -> impl Parser<'a, Loc>>, E> -where - F: Fn(Position) -> E, - F: Copy, - E: 'a, -{ - loc(map( - specialize_err(move |_, pos| to_expectation(pos), unqualified_ident()), - |n| Spaced::Item(ExposedName::new(n)), - )) -} - -#[inline(always)] -fn requires<'a>( -) -> impl Parser<'a, KeywordItem<'a, RequiresKeyword, PlatformRequires<'a>>, ERequires<'a>> { - record!(KeywordItem { - keyword: spaces_around_keyword( - RequiresKeyword, - ERequires::Requires, - ERequires::IndentRequires, - ERequires::IndentListStart - ), - item: platform_requires(), - }) -} - -#[inline(always)] -fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> { - record!(PlatformRequires { - rigids: skip_second(requires_rigids(), space0_e(ERequires::ListStart)), - signature: requires_typed_ident() - }) -} - -#[inline(always)] -fn requires_rigids<'a>( -) -> impl Parser<'a, Collection<'a, Loc>>>, ERequires<'a>> { - collection_trailing_sep_e( - byte(b'{', ERequires::ListStart), - specialize_err( - |_, pos| ERequires::Rigid(pos), - loc(map(ident::uppercase(), Spaced::Item)), - ), - byte(b',', ERequires::ListEnd), - byte(b'}', ERequires::ListEnd), - Spaced::SpaceBefore, - ) -} - -#[inline(always)] -fn requires_typed_ident<'a>() -> impl Parser<'a, Loc>>, ERequires<'a>> { - skip_first( - byte(b'{', ERequires::ListStart), - skip_second( - reset_min_indent(space0_around_ee( - specialize_err(ERequires::TypedIdent, loc(typed_ident())), - ERequires::ListStart, - ERequires::ListEnd, - )), - byte(b'}', ERequires::ListStart), - ), - ) -} - -#[inline(always)] -fn exposes_values_kw<'a>() -> impl Parser< - 'a, - KeywordItem<'a, ExposesKeyword, Collection<'a, Loc>>>>, - EExposes, -> { - record!(KeywordItem { - keyword: exposes_kw(), - item: exposes_list() - }) -} - -#[inline(always)] -fn exposes_kw<'a>() -> impl Parser<'a, Spaces<'a, ExposesKeyword>, EExposes> { - spaces_around_keyword( - ExposesKeyword, - EExposes::Exposes, - EExposes::IndentExposes, - EExposes::IndentListStart, - ) -} - -#[inline(always)] -fn exposes_list<'a>() -> impl Parser<'a, Collection<'a, Loc>>>, EExposes> -{ - collection_trailing_sep_e( - byte(b'[', EExposes::ListStart), - exposes_entry(EExposes::Identifier), - byte(b',', EExposes::ListEnd), - byte(b']', EExposes::ListEnd), - Spaced::SpaceBefore, - ) -} - -pub fn spaces_around_keyword<'a, K: Keyword, E>( - keyword_item: K, - expectation: fn(Position) -> E, - indent_problem1: fn(Position) -> E, - indent_problem2: fn(Position) -> E, -) -> impl Parser<'a, Spaces<'a, K>, E> -where - E: 'a + SpaceProblem, -{ - map( - and( - skip_second( - // parse any leading space before the keyword - backtrackable(space0_e(indent_problem1)), - // parse the keyword - crate::parser::keyword(K::KEYWORD, expectation), - ), - // parse the trailing space - space0_e(indent_problem2), - ), - move |(before, after)| Spaces { - before, - item: keyword_item, - after, - }, - ) -} - -#[inline(always)] -fn exposes_modules<'a>() -> impl Parser< - 'a, - KeywordItem<'a, ExposesKeyword, Collection<'a, Loc>>>>, - EExposes, -> { - record!(KeywordItem { - keyword: spaces_around_keyword( - ExposesKeyword, - EExposes::Exposes, - EExposes::IndentExposes, - EExposes::IndentListStart - ), - item: exposes_module_collection(), - }) -} - -fn exposes_module_collection<'a>( -) -> impl Parser<'a, Collection<'a, Loc>>>, EExposes> { - collection_trailing_sep_e( - byte(b'[', EExposes::ListStart), - exposes_module(EExposes::Identifier), - byte(b',', EExposes::ListEnd), - byte(b']', EExposes::ListEnd), - Spaced::SpaceBefore, - ) -} - -fn exposes_module<'a, F, E>( - to_expectation: F, -) -> impl Parser<'a, Loc>>, E> -where - F: Fn(Position) -> E, - F: Copy, - E: 'a, -{ - loc(map( - specialize_err(move |_, pos| to_expectation(pos), module_name()), - Spaced::Item, - )) -} - -#[inline(always)] -fn packages<'a>() -> impl Parser< - 'a, - KeywordItem<'a, PackagesKeyword, Collection<'a, Loc>>>>, - EPackages<'a>, -> { - record!(KeywordItem { - keyword: packages_kw(), - item: packages_collection() - }) -} - -#[inline(always)] -fn packages_kw<'a>() -> impl Parser<'a, Spaces<'a, PackagesKeyword>, EPackages<'a>> { - spaces_around_keyword( - PackagesKeyword, - EPackages::Packages, - EPackages::IndentPackages, - EPackages::IndentListStart, - ) -} - -#[inline(always)] -fn packages_collection<'a>( -) -> impl Parser<'a, Collection<'a, Loc>>>, EPackages<'a>> { - collection_trailing_sep_e( - byte(b'{', EPackages::ListStart), - specialize_err(EPackages::PackageEntry, loc(package_entry())), - byte(b',', EPackages::ListEnd), - byte(b'}', EPackages::ListEnd), - Spaced::SpaceBefore, - ) -} - -#[inline(always)] -fn generates<'a>( -) -> impl Parser<'a, KeywordItem<'a, GeneratesKeyword, UppercaseIdent<'a>>, EGenerates> { - record!(KeywordItem { - keyword: spaces_around_keyword( - GeneratesKeyword, - EGenerates::Generates, - EGenerates::IndentGenerates, - EGenerates::IndentTypeStart - ), - item: specialize_err(|(), pos| EGenerates::Identifier(pos), uppercase()) - }) -} - -#[inline(always)] -fn generates_with<'a>() -> impl Parser< - 'a, - KeywordItem<'a, WithKeyword, Collection<'a, Loc>>>>, - EGeneratesWith, -> { - record!(KeywordItem { - keyword: spaces_around_keyword( - WithKeyword, - EGeneratesWith::With, - EGeneratesWith::IndentWith, - EGeneratesWith::IndentListStart - ), - item: collection_trailing_sep_e( - byte(b'[', EGeneratesWith::ListStart), - exposes_entry(EGeneratesWith::Identifier), - byte(b',', EGeneratesWith::ListEnd), - byte(b']', EGeneratesWith::ListEnd), - Spaced::SpaceBefore - ) - }) -} - -#[inline(always)] -fn imports<'a>() -> impl Parser< - 'a, - KeywordItem<'a, ImportsKeyword, Collection<'a, Loc>>>>, - EImports, -> { - record!(KeywordItem { - keyword: spaces_around_keyword( - ImportsKeyword, - EImports::Imports, - EImports::IndentImports, - EImports::IndentListStart - ), - item: collection_trailing_sep_e( - byte(b'[', EImports::ListStart), - loc(imports_entry()), - byte(b',', EImports::ListEnd), - byte(b']', EImports::ListEnd), - Spaced::SpaceBefore - ) - }) - .trace("imports") -} - -#[inline(always)] -pub fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<'a>> { - // e.g. - // - // printLine : Str -> Effect {} - map( - and( - and( - loc(specialize_err( - |_, pos| ETypedIdent::Identifier(pos), - lowercase_ident(), - )), - space0_e(ETypedIdent::IndentHasType), - ), - skip_first( - byte(b':', ETypedIdent::HasType), - space0_before_e( - specialize_err( - ETypedIdent::Type, - reset_min_indent(type_annotation::located(true)), - ), - ETypedIdent::IndentType, - ), - ), - ), - |((ident, spaces_before_colon), ann)| { - Spaced::Item(TypedIdent { - ident, - spaces_before_colon, - ann, - }) - }, - ) -} - -fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> { - specialize_err(|_, pos| EImports::Shorthand(pos), lowercase_ident()) -} - -pub fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E> -where - F: Fn(Position) -> E, - E: 'a, - F: 'a, -{ - specialize_err(move |_, pos| to_expectation(pos), module_name()) -} - -#[inline(always)] -fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports> { - type Temp<'a> = ( - (Option<&'a str>, ModuleName<'a>), - Option>>>>, - ); - - let spaced_import = |((opt_shortname, module_name), opt_values): Temp<'a>| { - let exposed_values = opt_values.unwrap_or_else(Collection::empty); - - let entry = match opt_shortname { - Some(shortname) => ImportsEntry::Package(shortname, module_name, exposed_values), - - None => ImportsEntry::Module(module_name, exposed_values), - }; - - Spaced::Item(entry) - }; - - one_of!( - map( - and( - and( - // e.g. `pf.` - optional(backtrackable(skip_second( - shortname(), - byte(b'.', EImports::ShorthandDot) - ))), - // e.g. `Task` - module_name_help(EImports::ModuleName) - ), - // e.g. `.{ Task, after}` - optional(skip_first( - byte(b'.', EImports::ExposingDot), - collection_trailing_sep_e( - byte(b'{', EImports::SetStart), - exposes_entry(EImports::Identifier), - byte(b',', EImports::SetEnd), - byte(b'}', EImports::SetEnd), - Spaced::SpaceBefore - ) - )) - ), - spaced_import - ) - .trace("normal_import"), - map( - and( - and( - // e.g. "filename" - // TODO: str literal allows for multiline strings. We probably don't want that for file names. - specialize_err(|_, pos| EImports::StrLiteral(pos), parse_str_literal()), - // e.g. as - and( - and( - space0_e(EImports::AsKeyword), - two_bytes(b'a', b's', EImports::AsKeyword) - ), - space0_e(EImports::AsKeyword) - ) - ), - // e.g. file : Str - specialize_err(|_, pos| EImports::TypedIdent(pos), typed_ident()) - ), - |((file_name, _), typed_ident)| { - // TODO: look at blacking block strings during parsing. - Spaced::Item(ImportsEntry::IngestedFile(file_name, typed_ident)) - } - ) - .trace("ingest_file_import") - ) - .trace("imports_entry") -} diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index 67178b652bd..1deec69bcd5 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -5,12 +5,12 @@ use roc_region::all::{Loc, Position, Region}; use crate::{ ast::{ - AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, Header, Implements, - ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, ImportAsKeyword, - ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, - Module, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, PatternAs, - Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, - WhenBranch, + AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, Full, Header, + Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, + ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, + IngestedFileImport, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, + PatternAs, Spaced, Spaces, SpacesBefore, StrLiteral, StrSegment, Tag, TypeAnnotation, + TypeDef, TypeHeader, ValueDef, WhenBranch, }, header::{ AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, @@ -102,6 +102,24 @@ impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> { } } +impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for SpacesBefore<'a, V> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + SpacesBefore { + before: &[], + item: self.item.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Full<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + Full { + header: self.header.remove_spaces(arena), + defs: self.defs.remove_spaces(arena), + } + } +} + impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> { fn remove_spaces(&self, arena: &'a Bump) -> Self { KeywordItem { @@ -123,9 +141,9 @@ impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> { } } -impl<'a> RemoveSpaces<'a> for Module<'a> { +impl<'a> RemoveSpaces<'a> for Header<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { - let header = match &self.header { + match self { Header::Module(header) => Header::Module(ModuleHeader { after_keyword: &[], params: header.params.remove_spaces(arena), @@ -165,10 +183,6 @@ impl<'a> RemoveSpaces<'a> for Module<'a> { generates: header.generates.remove_spaces(arena), generates_with: header.generates_with.remove_spaces(arena), }), - }; - Module { - comments: &[], - header, } } } diff --git a/crates/compiler/parse/src/test_helpers.rs b/crates/compiler/parse/src/test_helpers.rs index 91d1f24b199..194bab9c59a 100644 --- a/crates/compiler/parse/src/test_helpers.rs +++ b/crates/compiler/parse/src/test_helpers.rs @@ -1,6 +1,8 @@ use crate::ast; use crate::ast::Defs; -use crate::module::parse_module_defs; +use crate::ast::Header; +use crate::ast::SpacesBefore; +use crate::header::parse_module_defs; use crate::parser::SourceError; use crate::parser::SyntaxError; use crate::state::State; @@ -39,10 +41,10 @@ pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result, pub fn parse_header_with<'a>( arena: &'a Bump, input: &'a str, -) -> Result, SyntaxError<'a>> { +) -> Result>, SyntaxError<'a>> { let state = State::new(input.as_bytes()); - match crate::module::parse_header(arena, state.clone()) { + match crate::header::parse_header(arena, state.clone()) { Ok((header, _)) => Ok(header), Err(fail) => Err(SyntaxError::Header(fail.problem)), } diff --git a/crates/compiler/parse/tests/test_parse.rs b/crates/compiler/parse/tests/test_parse.rs index ec0c1efe639..2e4e0deadb8 100644 --- a/crates/compiler/parse/tests/test_parse.rs +++ b/crates/compiler/parse/tests/test_parse.rs @@ -22,7 +22,7 @@ mod test_parse { use roc_parse::ast::StrSegment::*; use roc_parse::ast::{self, EscapedChar}; use roc_parse::ast::{CommentOrNewline, StrLiteral::*}; - use roc_parse::module::parse_module_defs; + use roc_parse::header::parse_module_defs; use roc_parse::parser::SyntaxError; use roc_parse::state::State; use roc_parse::test_helpers::parse_expr_with; diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index 64411b54e63..8ecba767cec 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -1,8 +1,8 @@ use bumpalo::Bump; -use roc_fmt::{annotation::Formattable, module::fmt_module}; +use roc_fmt::{annotation::Formattable, header::fmt_header}; use roc_parse::{ - ast::{Defs, Expr, Malformed, Module}, - module::parse_module_defs, + ast::{Defs, Expr, Full, Header, Malformed, SpacesBefore}, + header::parse_module_defs, parser::{Parser, SyntaxError}, remove_spaces::RemoveSpaces, state::State, @@ -70,16 +70,13 @@ impl InputOwned { /// Output AST of a successful parse #[derive(Debug, Clone)] pub enum Output<'a> { - Header(Module<'a>), + Header(SpacesBefore<'a, Header<'a>>), ModuleDefs(Defs<'a>), Expr(Expr<'a>), - Full { - header: Module<'a>, - module_defs: Defs<'a>, - }, + Full(Full<'a>), } impl<'a> Output<'a> { @@ -88,7 +85,7 @@ impl<'a> Output<'a> { let mut buf = Buf::new_in(&arena); match self { Output::Header(header) => { - fmt_module(&mut buf, header); + fmt_header(&mut buf, header); buf.fmt_end_of_file(); InputOwned::Header(buf.as_str().to_string()) } @@ -101,12 +98,9 @@ impl<'a> Output<'a> { expr.format(&mut buf, 0); InputOwned::Expr(buf.as_str().to_string()) } - Output::Full { - header, - module_defs, - } => { - fmt_module(&mut buf, header); - module_defs.format(&mut buf, 0); + Output::Full(full) => { + fmt_header(&mut buf, &full.header); + full.defs.format(&mut buf, 0); buf.fmt_end_of_file(); InputOwned::Full(buf.as_str().to_string()) } @@ -129,10 +123,7 @@ impl<'a> Malformed for Output<'a> { Output::Header(header) => header.is_malformed(), Output::ModuleDefs(defs) => defs.is_malformed(), Output::Expr(expr) => expr.is_malformed(), - Output::Full { - header, - module_defs, - } => header.is_malformed() || module_defs.is_malformed(), + Output::Full(full) => full.is_malformed(), } } } @@ -143,13 +134,7 @@ impl<'a> RemoveSpaces<'a> for Output<'a> { Output::Header(header) => Output::Header(header.remove_spaces(arena)), Output::ModuleDefs(defs) => Output::ModuleDefs(defs.remove_spaces(arena)), Output::Expr(expr) => Output::Expr(expr.remove_spaces(arena)), - Output::Full { - header, - module_defs, - } => Output::Full { - header: header.remove_spaces(arena), - module_defs: module_defs.remove_spaces(arena), - }, + Output::Full(full) => Output::Full(full.remove_spaces(arena)), } } } @@ -185,18 +170,19 @@ impl<'a> Input<'a> { let state = State::new(input.as_bytes()); let min_indent = 0; - let (_, header, state) = roc_parse::module::header() + let (_, header, state) = roc_parse::header::header() .parse(arena, state.clone(), min_indent) .map_err(|(_, fail)| SyntaxError::Header(fail))?; - let (header, defs) = header.upgrade_header_imports(arena); + let (new_header, defs) = header.item.upgrade_header_imports(arena); + let header = SpacesBefore { + before: header.before, + item: new_header, + }; - let module_defs = parse_module_defs(arena, state, defs)?; + let defs = parse_module_defs(arena, state, defs)?; - Ok(Output::Full { - header, - module_defs, - }) + Ok(Output::Full(Full { header, defs })) } } } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_app_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_app_header.header.result-ast index 800a60761ec..0b814c3e805 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_app_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_app_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: App( +SpacesBefore { + before: [], + item: App( AppHeader { before_provides: [], provides: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.result-ast index 2bd537602bd..f8fd0929b3c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Hosted( +SpacesBefore { + before: [], + item: Hosted( HostedHeader { before_name: [], name: @7-10 ModuleName( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.result-ast index 63a181db535..6307f56478c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: None, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.result-ast index 8dff2a08df5..a1e73806898 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Package( +SpacesBefore { + before: [], + item: Package( PackageHeader { before_exposes: [], exposes: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.result-ast index e93effb371e..51892ce9b2d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Platform( +SpacesBefore { + before: [], + item: Platform( PlatformHeader { before_name: [], name: @9-25 PackageName( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header.header.result-ast index f9f1093ed46..7484bead1ae 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: App( +SpacesBefore { + before: [], + item: App( AppHeader { before_provides: [], provides: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header_trailing_commas.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header_trailing_commas.header.result-ast index 0c5bfd7ec8b..6bb7c3c1866 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header_trailing_commas.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/full_app_header_trailing_commas.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: App( +SpacesBefore { + before: [], + item: App( AppHeader { before_provides: [], provides: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast index b607aa0d842..9b28638e99b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_effect_types.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Platform( +SpacesBefore { + before: [], + item: Platform( PlatformHeader { before_name: [], name: @9-14 PackageName( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.result-ast index d3a8418da9c..962f4a7a7cd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: App( +SpacesBefore { + before: [], + item: App( AppHeader { before_provides: [], provides: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_multiline_exposes.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_multiline_exposes.header.result-ast index 43e46e87555..edf4d77b77a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_multiline_exposes.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_multiline_exposes.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: None, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast index 6fd765312d3..bae7a81c1ac 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_multiline_params_and_exposes.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: Some( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.result-ast index 63a181db535..6307f56478c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: None, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast index 139cf2718db..b538f4f5c86 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_optional_param.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: Some( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast index a39e3e01bf9..64dc6a20cd6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: Some( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast index 5c31993249b..4bede1d7b9a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_params_and_multiline_exposes.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: Some( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast index 616fe065f8e..3b1fd0b179f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast @@ -1,81 +1,83 @@ -Full { - header: Module { - comments: [], - header: App( - AppHeader { - before_provides: [], - provides: [ - @5-9 ExposedName( - "main", - ), - ], - before_packages: [], - packages: @11-134 [ - @13-132 SpaceAfter( - PackageEntry { - shorthand: "pf", - spaces_after_shorthand: [ +Full( + Full { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [], + provides: [ + @5-9 ExposedName( + "main", + ), + ], + before_packages: [], + packages: @11-134 [ + @13-132 SpaceAfter( + PackageEntry { + shorthand: "pf", + spaces_after_shorthand: [ + Newline, + ], + platform_marker: None, + package_name: @17-132 PackageName( + "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", + ), + }, + [ Newline, ], - platform_marker: None, - package_name: @17-132 PackageName( - "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", - ), - }, - [ - Newline, - ], - ), - ], - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - module_defs: Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @136-183, - ], - space_before: [ - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 2, length = 1), - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @136-140 Identifier { - ident: "main", + ), + ], + old_imports: None, + old_provides_to_new_package: None, }, - @147-183 SpaceBefore( - Apply( - @147-158 Var { - module_name: "Stdout", - ident: "line", - }, - [ - @159-183 Str( - PlainLine( - "I'm a Roc application!", + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @136-183, + ], + space_before: [ + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @136-140 Identifier { + ident: "main", + }, + @147-183 SpaceBefore( + Apply( + @147-158 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @159-183 Str( + PlainLine( + "I'm a Roc application!", + ), ), - ), + ], + Space, + ), + [ + Newline, ], - Space, ), - [ - Newline, - ], ), - ), - ], + ], + }, }, -} +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_hosted_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_hosted_header.header.result-ast index 7688f73379a..a8b20a24dce 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_hosted_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_hosted_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Hosted( +SpacesBefore { + before: [], + item: Hosted( HostedHeader { before_name: [], name: @7-10 ModuleName( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_package_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_package_header.header.result-ast index 1fcdaa91593..0d80e8e4dc2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_package_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_package_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Package( +SpacesBefore { + before: [], + item: Package( PackageHeader { before_exposes: [], exposes: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_platform_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_platform_header.header.result-ast index 9a94accb8d5..0f01119e5e3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_platform_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nonempty_platform_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Platform( +SpacesBefore { + before: [], + item: Platform( PlatformHeader { before_name: [], name: @9-21 PackageName( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast index acb6368daa7..bbc9081993f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast @@ -1,117 +1,119 @@ -Full { - header: Module { - comments: [], - header: App( - AppHeader { - before_provides: [ - Newline, - ], - provides: [ - @143-147 ExposedName( - "main", - ), - ], - before_packages: [ - Newline, - ], - packages: @20-88 Collection { - items: [ - @44-81 SpaceBefore( - PackageEntry { - shorthand: "cli", - spaces_after_shorthand: [], - platform_marker: Some( - [], - ), - package_name: @49-81 PackageName( - "../basic-cli/platform/main.roc", - ), - }, - [ - Newline, - ], +Full( + Full { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [ + Newline, + ], + provides: [ + @143-147 ExposedName( + "main", ), ], - final_comments: [ + before_packages: [ Newline, ], - }, - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - module_defs: Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @111-121, - @157-187, - ], - space_before: [ - Slice(start = 0, length = 3), - Slice(start = 6, length = 2), - ], - space_after: [ - Slice(start = 3, length = 3), - Slice(start = 8, length = 2), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - ModuleImport( - ModuleImport { - before_name: [], - name: @111-121 ImportedModuleName { - package: Some( - "cli", - ), - name: ModuleName( - "Stdout", - ), + packages: @20-88 Collection { + items: [ + @44-81 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: Some( + [], + ), + package_name: @49-81 PackageName( + "../basic-cli/platform/main.roc", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], }, - params: None, - alias: None, - exposed: None, + old_imports: None, + old_provides_to_new_package: None, }, ), - Body( - @157-161 Identifier { - ident: "main", - }, - @168-187 SpaceBefore( - Apply( - @168-179 Var { - module_name: "Stdout", - ident: "line", + }, + defs: Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @111-121, + @157-187, + ], + space_before: [ + Slice(start = 0, length = 3), + Slice(start = 6, length = 2), + ], + space_after: [ + Slice(start = 3, length = 3), + Slice(start = 8, length = 2), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + ModuleImport( + ModuleImport { + before_name: [], + name: @111-121 ImportedModuleName { + package: Some( + "cli", + ), + name: ModuleName( + "Stdout", + ), }, - [ - @180-187 Str( - PlainLine( - "hello", + params: None, + alias: None, + exposed: None, + }, + ), + Body( + @157-161 Identifier { + ident: "main", + }, + @168-187 SpaceBefore( + Apply( + @168-179 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @180-187 Str( + PlainLine( + "hello", + ), ), - ), + ], + Space, + ), + [ + Newline, ], - Space, ), - [ - Newline, - ], ), - ), - ], + ], + }, }, -} +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_interface_header.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/old_interface_header.header.result-ast index f51600adada..bc9534217da 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_interface_header.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_interface_header.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Module( +SpacesBefore { + before: [], + item: Module( ModuleHeader { after_keyword: [], params: None, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.result-ast index 6dcb447fe46..c71493cd9a5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: App( +SpacesBefore { + before: [], + item: App( AppHeader { before_provides: [], provides: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/requires_type.header.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/requires_type.header.result-ast index 013bac0e27c..1641edbb837 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/requires_type.header.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/requires_type.header.result-ast @@ -1,6 +1,6 @@ -Module { - comments: [], - header: Platform( +SpacesBefore { + before: [], + item: Platform( PlatformHeader { before_name: [], name: @9-21 PackageName( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast index b3777d54930..d5de92e7647 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast @@ -1,71 +1,73 @@ -Full { - header: Module { - comments: [], - header: App( - AppHeader { - before_provides: [], - provides: [ - @6-10 ExposedName( - "main", - ), - ], - before_packages: [], - packages: @13-37 [ - @15-35 PackageEntry { - shorthand: "pf", - spaces_after_shorthand: [], - platform_marker: Some( - [], - ), - package_name: @29-35 PackageName( - "path", +Full( + Full { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [], + provides: [ + @6-10 ExposedName( + "main", ), - }, - ], - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - module_defs: Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @39-65, - ], - space_before: [ - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 2, length = 1), - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @39-43 Identifier { - ident: "main", + ], + before_packages: [], + packages: @13-37 [ + @15-35 PackageEntry { + shorthand: "pf", + spaces_after_shorthand: [], + platform_marker: Some( + [], + ), + package_name: @29-35 PackageName( + "path", + ), + }, + ], + old_imports: None, + old_provides_to_new_package: None, }, - @46-65 Apply( - @46-57 Var { - module_name: "Stdout", - ident: "line", + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @39-65, + ], + space_before: [ + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @39-43 Identifier { + ident: "main", }, - [ - @58-65 Str( - PlainLine( - "Hello", + @46-65 Apply( + @46-57 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @58-65 Str( + PlainLine( + "Hello", + ), ), - ), - ], - Space, + ], + Space, + ), ), - ), - ], + ], + }, }, -} +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast index 6f8c31abc05..06f84660f96 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast @@ -1,209 +1,211 @@ -Full { - header: Module { - comments: [], - header: App( - AppHeader { - before_provides: [], - provides: [ - @5-9 ExposedName( - "main", - ), - ], - before_packages: [], - packages: @11-55 Collection { - items: [ - @15-52 SpaceBefore( - PackageEntry { - shorthand: "cli", - spaces_after_shorthand: [], - platform_marker: None, - package_name: @20-52 PackageName( - "../basic-cli/platform/main.roc", - ), - }, - [ - Newline, - ], +Full( + Full { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [], + provides: [ + @5-9 ExposedName( + "main", ), ], - final_comments: [ - Newline, - ], - }, - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - module_defs: Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @57-74, - @76-220, - ], - space_before: [ - Slice(start = 0, length = 2), - Slice(start = 2, length = 2), - ], - space_after: [ - Slice(start = 2, length = 0), - Slice(start = 4, length = 2), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - ModuleImport( - ModuleImport { - before_name: [], - name: @64-74 ImportedModuleName { - package: Some( - "cli", - ), - name: ModuleName( - "Stdout", - ), + before_packages: [], + packages: @11-55 Collection { + items: [ + @15-52 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: None, + package_name: @20-52 PackageName( + "../basic-cli/platform/main.roc", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], }, - params: None, - alias: None, - exposed: None, + old_imports: None, + old_provides_to_new_package: None, }, ), - Body( - @76-80 Identifier { - ident: "main", - }, - @120-220 SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @120-133, - @162-205, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 3), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 3, length = 0), - ], - spaces: [ - Newline, - Newline, - LineComment( - " what about this?", - ), - ], - type_defs: [], - value_defs: [ - Stmt( - @120-133 BinOps( - [ - ( - @120-125 Str( - PlainLine( - "Foo", - ), - ), - @126-128 Pizza, - ), - ], - @129-132 TaskAwaitBang( - Var { - module_name: "A", - ident: "x", - }, - ), + }, + defs: Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @57-74, + @76-220, + ], + space_before: [ + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 2, length = 0), + Slice(start = 4, length = 2), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + ModuleImport( + ModuleImport { + before_name: [], + name: @64-74 ImportedModuleName { + package: Some( + "cli", + ), + name: ModuleName( + "Stdout", + ), + }, + params: None, + alias: None, + exposed: None, + }, + ), + Body( + @76-80 Identifier { + ident: "main", + }, + @120-220 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @120-133, + @162-205, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 3), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 3, length = 0), + ], + spaces: [ + Newline, + Newline, + LineComment( + " what about this?", ), - ), - Stmt( - @162-205 BinOps( - [ - ( - @162-167 Str( - PlainLine( - "Bar", + ], + type_defs: [], + value_defs: [ + Stmt( + @120-133 BinOps( + [ + ( + @120-125 Str( + PlainLine( + "Foo", + ), ), + @126-128 Pizza, ), - @168-170 Pizza, - ), - ], - @171-205 Apply( - @171-174 TaskAwaitBang( + ], + @129-132 TaskAwaitBang( Var { - module_name: "B", - ident: "y", + module_name: "A", + ident: "x", }, ), + ), + ), + Stmt( + @162-205 BinOps( [ - @185-205 SpaceBefore( - Record( - [ - @187-203 RequiredValue( - @187-193 "config", - [], - @195-203 Str( - PlainLine( - "config", + ( + @162-167 Str( + PlainLine( + "Bar", + ), + ), + @168-170 Pizza, + ), + ], + @171-205 Apply( + @171-174 TaskAwaitBang( + Var { + module_name: "B", + ident: "y", + }, + ), + [ + @185-205 SpaceBefore( + Record( + [ + @187-203 RequiredValue( + @187-193 "config", + [], + @195-203 Str( + PlainLine( + "config", + ), ), ), - ), + ], + ), + [ + Newline, ], ), - [ - Newline, - ], - ), - ], - Space, + ], + Space, + ), ), ), + ], + }, + @211-220 SpaceBefore( + Apply( + @211-214 Var { + module_name: "C", + ident: "z", + }, + [ + @215-220 Str( + PlainLine( + "Bar", + ), + ), + ], + Space, ), - ], - }, - @211-220 SpaceBefore( - Apply( - @211-214 Var { - module_name: "C", - ident: "z", - }, [ - @215-220 Str( - PlainLine( - "Bar", - ), - ), + Newline, + Newline, ], - Space, ), - [ - Newline, - Newline, - ], ), + [ + Newline, + LineComment( + " is this a valid statement?", + ), + ], ), - [ - Newline, - LineComment( - " is this a valid statement?", - ), - ], ), - ), - ], + ], + }, }, -} +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast index c4e24e09b64..f6ddc99639e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast @@ -1,131 +1,133 @@ -Full { - header: Module { - comments: [], - header: App( - AppHeader { - before_provides: [ - Newline, - ], - provides: [ - @74-78 ExposedName( - "main", - ), - ], - before_packages: [ - Newline, - ], - packages: @6-44 Collection { - items: [ - @30-37 SpaceBefore( - PackageEntry { - shorthand: "cli", - spaces_after_shorthand: [], - platform_marker: Some( - [], - ), - package_name: @35-37 PackageName( - "", - ), - }, - [ - Newline, - ], +Full( + Full { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [ + Newline, + ], + provides: [ + @74-78 ExposedName( + "main", ), ], - final_comments: [ + before_packages: [ Newline, ], + packages: @6-44 Collection { + items: [ + @30-37 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: Some( + [], + ), + package_name: @35-37 PackageName( + "", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + old_imports: None, + old_provides_to_new_package: None, }, - old_imports: None, - old_provides_to_new_package: None, - }, - ), - }, - module_defs: Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @88-202, - ], - space_before: [ - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 2, length = 1), - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @88-92 Identifier { - ident: "main", - }, - @100-202 SpaceBefore( - BinOps( - [ - ( - @100-114 SpaceAfter( - Str( - PlainLine( - "jq --version", + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @88-202, + ], + space_before: [ + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @88-92 Identifier { + ident: "main", + }, + @100-202 SpaceBefore( + BinOps( + [ + ( + @100-114 SpaceAfter( + Str( + PlainLine( + "jq --version", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], + @123-125 Pizza, ), - @123-125 Pizza, - ), - ( - @126-133 SpaceAfter( - Var { - module_name: "Cmd", - ident: "new", - }, - [ - Newline, - ], + ( + @126-133 SpaceAfter( + Var { + module_name: "Cmd", + ident: "new", + }, + [ + Newline, + ], + ), + @142-144 Pizza, ), - @142-144 Pizza, - ), - ( - @145-155 SpaceAfter( + ( + @145-155 SpaceAfter( + Var { + module_name: "Cmd", + ident: "status", + }, + [ + Newline, + ], + ), + @164-166 Pizza, + ), + ], + @167-202 Apply( + @167-178 TaskAwaitBang( Var { - module_name: "Cmd", - ident: "status", + module_name: "Task", + ident: "mapErr", }, - [ - Newline, - ], ), - @164-166 Pizza, - ), - ], - @167-202 Apply( - @167-178 TaskAwaitBang( - Var { - module_name: "Task", - ident: "mapErr", - }, + [ + @180-202 Tag( + "UnableToCheckJQVersion", + ), + ], + Space, ), - [ - @180-202 Tag( - "UnableToCheckJQVersion", - ), - ], - Space, ), + [ + Newline, + ], ), - [ - Newline, - ], ), - ), - ], + ], + }, }, -} +) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 434b343f98f..a01bae139e9 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -5,10 +5,10 @@ extern crate indoc; mod test_fmt { use bumpalo::Bump; use roc_fmt::def::fmt_defs; - use roc_fmt::module::fmt_module; + use roc_fmt::header::fmt_header; use roc_fmt::Buf; - use roc_parse::ast::{Defs, Module}; - use roc_parse::module::{self, parse_module_defs}; + use roc_parse::ast::{Defs, Header, SpacesBefore}; + use roc_parse::header::{self, parse_module_defs}; use roc_parse::state::State; use roc_test_utils::assert_multiline_str_eq; use roc_test_utils_dir::workspace_root; @@ -32,11 +32,11 @@ mod test_fmt { fn fmt_module_and_defs<'a>( arena: &Bump, src: &str, - module: &Module<'a>, + header: &SpacesBefore<'a, Header<'a>>, state: State<'a>, buf: &mut Buf<'_>, ) { - fmt_module(buf, module); + fmt_header(buf, header); match parse_module_defs(arena, state, Defs::default()) { Ok(loc_defs) => { @@ -61,7 +61,7 @@ mod test_fmt { let src = src.trim(); let expected = expected.trim(); - match module::parse_header(&arena, State::new(src.as_bytes())) { + match header::parse_header(&arena, State::new(src.as_bytes())) { Ok((actual, state)) => { use roc_parse::remove_spaces::RemoveSpaces; @@ -71,7 +71,7 @@ mod test_fmt { let output = buf.as_str().trim(); - let (reparsed_ast, state) = module::parse_header(&arena, State::new(output.as_bytes())).unwrap_or_else(|err| { + let (reparsed_ast, state) = header::parse_header(&arena, State::new(output.as_bytes())).unwrap_or_else(|err| { panic!( "After formatting, the source code no longer parsed!\n\nParse error was: {err:?}\n\nThe code that failed to parse:\n\n{output}\n\n" ); diff --git a/crates/language_server/src/analysis/parse_ast.rs b/crates/language_server/src/analysis/parse_ast.rs index ac751c3e9eb..aeac7957650 100644 --- a/crates/language_server/src/analysis/parse_ast.rs +++ b/crates/language_server/src/analysis/parse_ast.rs @@ -1,8 +1,8 @@ use bumpalo::Bump; use roc_fmt::Buf; use roc_parse::{ - ast::{Defs, Module}, - module::parse_module_defs, + ast::{Defs, Header, SpacesBefore}, + header::parse_module_defs, parser::SyntaxError, }; use roc_region::all::Loc; @@ -15,23 +15,26 @@ mod format; pub struct Ast<'a> { arena: &'a Bump, - module: Module<'a>, + module: SpacesBefore<'a, Header<'a>>, defs: Defs<'a>, } impl<'a> Ast<'a> { pub fn parse(arena: &'a Bump, src: &'a str) -> Result, SyntaxError<'a>> { - use roc_parse::{module::parse_header, state::State}; + use roc_parse::{header::parse_header, state::State}; let (module, state) = parse_header(arena, State::new(src.as_bytes())) .map_err(|e| SyntaxError::Header(e.problem))?; - let (module, defs) = module.upgrade_header_imports(arena); + let (header, defs) = module.item.upgrade_header_imports(arena); let defs = parse_module_defs(arena, state, defs)?; Ok(Ast { - module, + module: SpacesBefore { + before: module.before, + item: header, + }, defs, arena, }) @@ -40,7 +43,7 @@ impl<'a> Ast<'a> { pub fn fmt(&self) -> FormattedAst<'a> { let mut buf = Buf::new_in(self.arena); - roc_fmt::module::fmt_module(&mut buf, &self.module); + roc_fmt::header::fmt_header(&mut buf, &self.module); roc_fmt::def::fmt_defs(&mut buf, &self.defs, 0); @@ -50,7 +53,7 @@ impl<'a> Ast<'a> { } pub fn semantic_tokens(&self) -> impl IntoIterator> + '_ { - let header_tokens = self.module.iter_tokens(self.arena); + let header_tokens = self.module.item.iter_tokens(self.arena); let body_tokens = self.defs.iter_tokens(self.arena); header_tokens.into_iter().chain(body_tokens) diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index f297e0e5788..d34c155bfe9 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -6,8 +6,8 @@ use roc_module::called_via::{BinOp, UnaryOp}; use roc_parse::{ ast::{ AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, Header, Implements, - ImplementsAbilities, ImplementsAbility, ImplementsClause, Module, OldRecordBuilderField, - Pattern, PatternAs, Spaced, StrLiteral, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, + ImplementsAbilities, ImplementsAbility, ImplementsClause, OldRecordBuilderField, Pattern, + PatternAs, Spaced, StrLiteral, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch, }, header::{ @@ -189,16 +189,6 @@ impl IterTokens for (T, U) { } } -impl IterTokens for Module<'_> { - fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc> { - let Self { - comments: _, - header, - } = self; - header.iter_tokens(arena) - } -} - impl IterTokens for Header<'_> { fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc> { match self { diff --git a/crates/packaging/src/tarball.rs b/crates/packaging/src/tarball.rs index 088f1081fc0..165ed8a90ee 100644 --- a/crates/packaging/src/tarball.rs +++ b/crates/packaging/src/tarball.rs @@ -2,10 +2,10 @@ use brotli::enc::BrotliEncoderParams; use bumpalo::Bump; use flate2::write::GzEncoder; use roc_parse::ast::{ - Header, IngestedFileImport, Module, RecursiveValueDefIter, StrLiteral, ValueDef, + Header, IngestedFileImport, RecursiveValueDefIter, SpacesBefore, StrLiteral, ValueDef, }; use roc_parse::header::PlatformHeader; -use roc_parse::module::{parse_header, parse_module_defs}; +use roc_parse::header::{parse_header, parse_module_defs}; use roc_parse::state::State; use std::ffi::OsStr; use std::fs::File; @@ -129,7 +129,7 @@ fn write_archive(path: &Path, writer: W) -> io::Result<()> { // TODO use this when finding .roc files by discovering them from the root module. // let other_modules: &[Module<'_>] = - match read_header(&arena, &mut buf, path)?.0.header { + match read_header(&arena, &mut buf, path)?.0.item { Header::Module(_) => { todo!(); // TODO report error @@ -261,7 +261,7 @@ fn read_header<'a>( arena: &'a Bump, buf: &'a mut Vec, path: &'a Path, -) -> io::Result<(Module<'a>, State<'a>)> { +) -> io::Result<(SpacesBefore<'a, Header<'a>>, State<'a>)> { // Read all the bytes into the buffer. { let mut file = File::open(path)?; @@ -287,8 +287,8 @@ fn add_ingested_files( builder: &mut tar::Builder, ) -> io::Result<()> { let mut buf = Vec::new(); - let (module, state) = read_header(arena, &mut buf, dot_roc_path)?; - let (_, defs) = module.upgrade_header_imports(arena); + let (header, state) = read_header(arena, &mut buf, dot_roc_path)?; + let (_, defs) = header.item.upgrade_header_imports(arena); let defs = parse_module_defs(arena, state, defs).unwrap_or_else(|err| { panic!("{} failed to parse: {:?}", dot_roc_path.display(), err); From 1a09a5347dff5e41c6bff1c403b0668d5bab3c12 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 9 Aug 2024 14:30:00 +0200 Subject: [PATCH 154/203] more debug tips Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- CONTRIBUTING.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b85d55191e..2c5add8d7d9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,14 +45,17 @@ Execute `cargo fmt --all` to fix the formatting.
:beetle: Debugging Tips + +- When using github search to find similar errors/issues use `org:roc-lang`, for example `org:roc-lang valgrind unrecognised instruction`. This will search in basic-cli, basic-webserver, ... as well. Just using `roc` instead of `org:roc-lang` may yield useful results as well. - Use a debug build of the compiler. We have many asserts enabled in the debug compiler that can alert you to something going wrong. When building from source, build the debug compiler with `cargo build --bin roc`, the binary is at roc/target/debug/roc. When using roc through a nix flake like in [basic-cli](https://github.com/roc-lang/basic-cli), use `rocPkgs.cli-debug` instead of `rocPkgs.cli`. -- At the bottom of [.cargo/config.toml](https://github.com/roc-lang/roc/blob/main/.cargo/config.toml) we have useful debug flags that activate certain debug prints. +- At the bottom of [.cargo/config.toml](https://github.com/roc-lang/roc/blob/main/.cargo/config.toml) we have useful debug flags that activate certain debug prints and extra checks. - For Roc code; minimize the code that produces the issue. - If you plan to look at the data used and produced inside the compiler, try to reproduce your issue with a very simple platform like our [minimal Rust platform](https://github.com/roc-lang/roc/tree/main/examples/platform-switching/rust-platform) instead of for example basic-cli. - For segmentation faults: + In general we recommend using linux to investigate, it has better tools for this. - + Use `roc build myApp.roc --linker=legacy` followed by `valgrind ./myApp`. + + If your segfault also happens when using `--linker=legacy`, using it will improve valgrind output. Use `roc build myApp.roc --linker=legacy` followed by `valgrind ./myApp`. + Use gdb to step through the code, [this gdb script](https://roc.zulipchat.com/#narrow/stream/395097-compiler-development/topic/gdb.20script/near/424422545) can be helpful. + + Use objdump to look at the assembly of the code, for example `objdump -d -M intel ./examples/Arithmetic/main`. Replace `-M intel` with the appropriate flag for your CPU. + Inspect the generated LLVM IR (`roc build myApp.roc --emit-llvm-ir`) between Roc code that encounters the segfault and code that doesn't. From bf63c79997bb9a8675f1e8967be998c54084aa95 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 9 Aug 2024 15:47:54 +0200 Subject: [PATCH 155/203] move debug tips --- CONTRIBUTING.md | 18 ++---------------- devtools/debug_tips.md | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 16 deletions(-) create mode 100644 devtools/debug_tips.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c5add8d7d9..0c6dab5572b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,23 +43,9 @@ Execute `cargo fmt --all` to fix the formatting. - The [compiler's README](https://github.com/roc-lang/roc/tree/main/crates/compiler) contains important info. - The AI chat in the [cursor editor](https://www.cursor.com/) can also help you find your way in the codebase. -
-:beetle: Debugging Tips - -- When using github search to find similar errors/issues use `org:roc-lang`, for example `org:roc-lang valgrind unrecognised instruction`. This will search in basic-cli, basic-webserver, ... as well. Just using `roc` instead of `org:roc-lang` may yield useful results as well. -- Use a debug build of the compiler. We have many asserts enabled in the debug compiler that can alert you to something going wrong. When building from source, build the debug compiler with `cargo build --bin roc`, the binary is at roc/target/debug/roc. When using roc through a nix flake like in [basic-cli](https://github.com/roc-lang/basic-cli), use `rocPkgs.cli-debug` instead of `rocPkgs.cli`. -- At the bottom of [.cargo/config.toml](https://github.com/roc-lang/roc/blob/main/.cargo/config.toml) we have useful debug flags that activate certain debug prints and extra checks. -- For Roc code; minimize the code that produces the issue. -- If you plan to look at the data used and produced inside the compiler, try to reproduce your issue with a very simple platform like our [minimal Rust platform](https://github.com/roc-lang/roc/tree/main/examples/platform-switching/rust-platform) instead of for example basic-cli. -- For segmentation faults: - + In general we recommend using linux to investigate, it has better tools for this. - + If your segfault also happens when using `--linker=legacy`, using it will improve valgrind output. Use `roc build myApp.roc --linker=legacy` followed by `valgrind ./myApp`. - + Use gdb to step through the code, [this gdb script](https://roc.zulipchat.com/#narrow/stream/395097-compiler-development/topic/gdb.20script/near/424422545) can be helpful. - + Use objdump to look at the assembly of the code, for example `objdump -d -M intel ./examples/Arithmetic/main`. Replace `-M intel` with the appropriate flag for your CPU. - + Inspect the generated LLVM IR (`roc build myApp.roc --emit-llvm-ir`) between Roc code that encounters the segfault and code that doesn't. - +### Debugging tips -
+If you need to do some debugging, check out [our tips](devtools/debug_tips.md). ### Commit signing diff --git a/devtools/debug_tips.md b/devtools/debug_tips.md new file mode 100644 index 00000000000..afd0918a336 --- /dev/null +++ b/devtools/debug_tips.md @@ -0,0 +1,17 @@ +# Debug Tips + +## General + +- When using github search to find similar errors/issues use `org:roc-lang`, for example: `org:roc-lang valgrind unrecognised instruction`. This will search in basic-cli, basic-webserver, ... as well. Just using `roc` instead of `org:roc-lang` may yield useful results as well. +- Use a debug build of the compiler. We have many asserts enabled in the debug compiler that can alert you to something going wrong. When building from source, build the debug compiler with `cargo build --bin roc`, the binary is at `roc/target/debug/roc`. When using roc through a nix flake like in [basic-cli](https://github.com/roc-lang/basic-cli), use `rocPkgs.cli-debug` instead of `rocPkgs.cli`. +- At the bottom of [.cargo/config.toml](https://github.com/roc-lang/roc/blob/main/.cargo/config.toml) we have useful debug flags that activate certain debug prints and extra checks. +- For Roc code; minimize the code that produces the issue. +- If you plan to look at the data used and produced inside the compiler, try to reproduce your issue with a very simple platform like our [minimal Rust platform](https://github.com/roc-lang/roc/tree/main/examples/platform-switching/rust-platform) instead of for example basic-cli. + +## Segmentation Faults + +- In general we recommend using linux to investigate, it has better tools for this. +- If your segfault also happens when using `--linker=legacy`, use it to improve valgrind output. For example: `roc build myApp.roc --linker=legacy` followed by `valgrind ./myApp`. +- Use gdb to step through the code, [this gdb script](https://roc.zulipchat.com/#narrow/stream/395097-compiler-development/topic/gdb.20script/near/424422545) can be helpful. +- Use objdump to look at the assembly of the code, for example `objdump -d -M intel ./examples/Arithmetic/main`. Replace `-M intel` with the appropriate flag for your CPU. +- Inspect the generated LLVM IR (`roc build myApp.roc --emit-llvm-ir`) between Roc code that encounters the segfault and code that doesn't. \ No newline at end of file From a7f66c4676fe2a96e70c8679a59afd5f4fbeed03 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:15:03 +0200 Subject: [PATCH 156/203] ci check debug flags --- .cargo/config.toml | 3 ++- .github/workflows/ubuntu_x86_64_debug.yml | 1 + ci/check_debug_vars.sh | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100755 ci/check_debug_vars.sh diff --git a/.cargo/config.toml b/.cargo/config.toml index 81e24566c43..f57d96eaf7d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -30,7 +30,7 @@ rustflags = ["-Clink-args=/FORCE:UNRESOLVED"] # https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993 ROC_WORKSPACE_DIR = { value = "", relative = true } -# Debug flags. Keep this up-to-date with compiler/debug_flags/src/lib.rs. +# Debug flags. Explanations for these are in compiler/debug_flags/src/lib.rs. # Set = "1" to turn a debug flag on. ROC_PRETTY_PRINT_ALIAS_CONTENTS = "0" ROC_PRINT_UNIFICATIONS = "0" @@ -49,6 +49,7 @@ ROC_PRINT_IR_AFTER_TRMC = "0" ROC_PRINT_IR_AFTER_DROP_SPECIALIZATION = "0" ROC_DEBUG_ALIAS_ANALYSIS = "0" ROC_PRINT_RUNTIME_ERROR_GEN = "0" +ROC_NO_UNBOUND_LAYOUT = "0" ROC_PRINT_LLVM_FN_VERIFICATION = "0" ROC_WRITE_FINAL_WASM = "0" ROC_LOG_WASM_INTERP = "0" diff --git a/.github/workflows/ubuntu_x86_64_debug.yml b/.github/workflows/ubuntu_x86_64_debug.yml index 2ad30f07522..7524f2bfcb7 100644 --- a/.github/workflows/ubuntu_x86_64_debug.yml +++ b/.github/workflows/ubuntu_x86_64_debug.yml @@ -35,4 +35,5 @@ jobs: - name: cargo test without --release env: RUSTFLAGS: -C link-arg=-fuse-ld=lld + ROC_CHECK_MONO_IR: 1 run: cargo test -- --skip tests/exhaustive/match_on_result_with_uninhabited_error_destructuring_in_lambda_syntax.txt --skip tests::identity_lambda --skip tests::issue_2300 --skip tests::issue_2582_specialize_result_value --skip tests::sum_lambda diff --git a/ci/check_debug_vars.sh b/ci/check_debug_vars.sh new file mode 100755 index 00000000000..0d54a3d7ce4 --- /dev/null +++ b/ci/check_debug_vars.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ +set -euo pipefail + +# Extract vars from .cargo/config.toml +config_vars=$(grep -E "^ROC_.*= \"[01]\"" .cargo/config.toml | cut -d'=' -f1 | tr -d ' ') + +# Extract vars from crates/compiler/debug_flags/src/lib.rs +lib_vars=$(grep -E "^ ROC_.*" crates/compiler/debug_flags/src/lib.rs | tr -d ' ') + +# Sort both lists +sorted_config_vars=$(echo "$config_vars" | sort) +sorted_lib_vars=$(echo "$lib_vars" | sort) + +# Compare the sorted lists +if diff <(echo "$sorted_config_vars") <(echo "$sorted_lib_vars") > /dev/null; then + echo "The flags in both files are identical." +else + echo "Looks like some flags are out of sync between .cargo/config.toml and crates/compiler/debug_flags/src/lib.rs:" + diff <(echo "$sorted_config_vars") <(echo "$sorted_lib_vars") +fi \ No newline at end of file From 6fde123ea6170420974519e9d2ebccd6c30f044e Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:18:01 +0200 Subject: [PATCH 157/203] actually run check --- .github/workflows/ubuntu_x86_64_debug.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ubuntu_x86_64_debug.yml b/.github/workflows/ubuntu_x86_64_debug.yml index 7524f2bfcb7..0be11ffad91 100644 --- a/.github/workflows/ubuntu_x86_64_debug.yml +++ b/.github/workflows/ubuntu_x86_64_debug.yml @@ -31,6 +31,9 @@ jobs: sudo ln -s /usr/bin/lld-16 /usr/bin/ld.lld sudo apt -y install libpolly-16-dev + - name: Check if debug flag files are in sync + run: ./ci/check_debug_vars.sh + # for skipped tests; see #6946, #6947 - name: cargo test without --release env: From 635e6058cce4961e6c4fa7363545b794a44f1818 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 10 Aug 2024 12:28:37 +0200 Subject: [PATCH 158/203] allow glue check toggle --- crates/glue/src/types.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index d1b9e7576b9..0a1bd273205 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -2219,21 +2219,24 @@ fn single_tag_payload_fields<'a, 'b>( types: &mut Types, ) -> (String, RocSingleTagPayload) { let layout = env.layout_cache.interner.get(in_layout); - // There should be a glue_procs_by_layout entry iff this layout has a closure in it, - // so we shouldn't need to separately check that. Howeevr, we still do a debug_assert - // anyway just so we have some warning in case that relationship somehow didn't hold! - debug_assert_eq!( - env.glue_procs_by_layout.get(&layout).is_some(), - env.layout_cache - .interner - .has_varying_stack_size(in_layout, env.arena), - "glue_procs_by_layout for {:?} was {:?}, but the layout cache said its has_varying_stack_size was {}", - &layout, - env.glue_procs_by_layout.get(&layout), + + if std::env::var_os("SINGLE_TAG_GLUE_CHECK_OFF").as_deref() != Some("1") { + // There should be a glue_procs_by_layout entry iff this layout has a closure in it, + // so we shouldn't need to separately check that. Howeevr, we still do a debug_assert + // anyway just so we have some warning in case that relationship somehow didn't hold! + debug_assert_eq!( + env.glue_procs_by_layout.get(&layout).is_some(), env.layout_cache .interner - .has_varying_stack_size(in_layout, env.arena) - ); + .has_varying_stack_size(in_layout, env.arena), + "glue_procs_by_layout for {:?} was {:?}, but the layout cache said its has_varying_stack_size was {}", + &layout, + env.glue_procs_by_layout.get(&layout), + env.layout_cache + .interner + .has_varying_stack_size(in_layout, env.arena) + ); + } let (tag_name, payload_vars) = single_tag_payload(union_tags, subs); From 12f9bacf6ec95038a6daf63b10bf968ef86a833d Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 10 Aug 2024 12:39:02 +0200 Subject: [PATCH 159/203] fix errors --- crates/glue/src/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/glue/src/types.rs b/crates/glue/src/types.rs index 0a1bd273205..d90bd744354 100644 --- a/crates/glue/src/types.rs +++ b/crates/glue/src/types.rs @@ -2220,7 +2220,7 @@ fn single_tag_payload_fields<'a, 'b>( ) -> (String, RocSingleTagPayload) { let layout = env.layout_cache.interner.get(in_layout); - if std::env::var_os("SINGLE_TAG_GLUE_CHECK_OFF").as_deref() != Some("1") { + if std::env::var("SINGLE_TAG_GLUE_CHECK_OFF").as_deref() != Ok("1") { // There should be a glue_procs_by_layout entry iff this layout has a closure in it, // so we shouldn't need to separately check that. Howeevr, we still do a debug_assert // anyway just so we have some warning in case that relationship somehow didn't hold! From 439ebcddce8910eaa684b58aa661cac3fc6bf5d5 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 10 Aug 2024 14:31:29 +0200 Subject: [PATCH 160/203] make target an argument --- crates/cli/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 982358d03a5..b0b2e08e1de 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -432,6 +432,14 @@ pub fn build_app() -> Command { .action(ArgAction::SetTrue) .required(false) ) + .arg( + Arg::new(FLAG_TARGET) + .long(FLAG_TARGET) + .help("Choose a different target") + .default_value(Into::<&'static str>::into(Target::default())) + .value_parser(build_target_values_parser.clone()) + .required(false), + ) ) .arg(flag_optimize) .arg(flag_max_threads) From 0abea91796f12d30698bd063f606a98c976a9101 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 10 Aug 2024 13:20:54 -0700 Subject: [PATCH 161/203] Rename Full -> FullAst --- crates/cli/src/format.rs | 8 ++++---- crates/compiler/parse/src/ast.rs | 4 ++-- crates/compiler/parse/src/remove_spaces.rs | 6 +++--- crates/compiler/test_syntax/src/test_helpers.rs | 6 +++--- .../snapshots/pass/newline_in_packages.full.result-ast | 2 +- .../tests/snapshots/pass/old_app_header.full.result-ast | 2 +- .../snapshots/pass/space_before_colon.full.result-ast | 2 +- .../tests/snapshots/pass/suffixed_one_def.full.result-ast | 2 +- .../snapshots/pass/suffixed_optional_last.full.result-ast | 2 +- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index 2eec0782b1d..c31958de651 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -7,7 +7,7 @@ use roc_error_macros::{internal_error, user_error}; use roc_fmt::def::fmt_defs; use roc_fmt::header::fmt_header; use roc_fmt::Buf; -use roc_parse::ast::{Full, SpacesBefore}; +use roc_parse::ast::{FullAst, SpacesBefore}; use roc_parse::header::parse_module_defs; use roc_parse::remove_spaces::RemoveSpaces; use roc_parse::{header, parser::SyntaxError, state::State}; @@ -231,7 +231,7 @@ pub fn format_src(arena: &Bump, src: &str) -> Result { Ok(buf.as_str().to_string()) } -fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError<'a>> { +fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError<'a>> { let (header, state) = header::parse_header(arena, State::new(src.as_bytes())) .map_err(|e| SyntaxError::Header(e.problem))?; @@ -239,7 +239,7 @@ fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError< let defs = parse_module_defs(arena, state, defs)?; - Ok(Full { + Ok(FullAst { header: SpacesBefore { before: header.before, item: h, @@ -248,7 +248,7 @@ fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result, SyntaxError< }) } -fn fmt_all<'a>(buf: &mut Buf<'a>, ast: &'a Full) { +fn fmt_all<'a>(buf: &mut Buf<'a>, ast: &'a FullAst) { fmt_header(buf, &ast.header); fmt_defs(buf, &ast.defs, 0); diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index e9effe63b32..296efc8307f 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -15,7 +15,7 @@ use roc_module::ident::QualifiedModuleName; use roc_region::all::{Loc, Position, Region}; #[derive(Debug, Clone)] -pub struct Full<'a> { +pub struct FullAst<'a> { pub header: SpacesBefore<'a, Header<'a>>, pub defs: Defs<'a>, } @@ -2436,7 +2436,7 @@ pub trait Malformed { fn is_malformed(&self) -> bool; } -impl<'a> Malformed for Full<'a> { +impl<'a> Malformed for FullAst<'a> { fn is_malformed(&self) -> bool { self.header.item.is_malformed() || self.defs.is_malformed() } diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index 1deec69bcd5..b63571bb0aa 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -5,7 +5,7 @@ use roc_region::all::{Loc, Position, Region}; use crate::{ ast::{ - AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, Full, Header, + AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, FullAst, Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, @@ -111,9 +111,9 @@ impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for SpacesBefore<'a, V> { } } -impl<'a> RemoveSpaces<'a> for Full<'a> { +impl<'a> RemoveSpaces<'a> for FullAst<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { - Full { + FullAst { header: self.header.remove_spaces(arena), defs: self.defs.remove_spaces(arena), } diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index 8ecba767cec..140e1092499 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -1,7 +1,7 @@ use bumpalo::Bump; use roc_fmt::{annotation::Formattable, header::fmt_header}; use roc_parse::{ - ast::{Defs, Expr, Full, Header, Malformed, SpacesBefore}, + ast::{Defs, Expr, FullAst, Header, Malformed, SpacesBefore}, header::parse_module_defs, parser::{Parser, SyntaxError}, remove_spaces::RemoveSpaces, @@ -76,7 +76,7 @@ pub enum Output<'a> { Expr(Expr<'a>), - Full(Full<'a>), + Full(FullAst<'a>), } impl<'a> Output<'a> { @@ -182,7 +182,7 @@ impl<'a> Input<'a> { let defs = parse_module_defs(arena, state, defs)?; - Ok(Output::Full(Full { header, defs })) + Ok(Output::Full(FullAst { header, defs })) } } } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast index 3b1fd0b179f..7e12303321b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast @@ -1,5 +1,5 @@ Full( - Full { + FullAst { header: SpacesBefore { before: [], item: App( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast index bbc9081993f..abf05b26871 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast @@ -1,5 +1,5 @@ Full( - Full { + FullAst { header: SpacesBefore { before: [], item: App( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast index d5de92e7647..09323e239d8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast @@ -1,5 +1,5 @@ Full( - Full { + FullAst { header: SpacesBefore { before: [], item: App( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast index 06f84660f96..b124b30b99f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast @@ -1,5 +1,5 @@ Full( - Full { + FullAst { header: SpacesBefore { before: [], item: App( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast index f6ddc99639e..78f12e6ffd3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast @@ -1,5 +1,5 @@ Full( - Full { + FullAst { header: SpacesBefore { before: [], item: App( From 6835041a613cbee4d661dc244316148a7bc835a7 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 10 Aug 2024 12:40:18 -0700 Subject: [PATCH 162/203] Normalize string encoding as part of RemoveSpaces --- crates/compiler/parse/src/remove_spaces.rs | 97 +++++++++++++++++++++- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs index b63571bb0aa..66e45e3cfd9 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -1,4 +1,4 @@ -use bumpalo::collections::Vec; +use bumpalo::collections::{String, Vec}; use bumpalo::Bump; use roc_module::called_via::{BinOp, UnaryOp}; use roc_region::all::{Loc, Position, Region}; @@ -598,12 +598,103 @@ impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { match *self { StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t), - StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)), - StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)), + StrLiteral::Line(t) => { + let mut needs_merge = false; + let mut last_was_mergable = false; + for segment in t.iter() { + let mergable = matches!( + segment, + StrSegment::Plaintext(_) + | StrSegment::Unicode(_) + | StrSegment::EscapedChar(_) + ); + if mergable && last_was_mergable { + needs_merge = true; + break; + } + last_was_mergable = mergable; + } + + if !needs_merge { + return StrLiteral::Line(t.remove_spaces(arena)); + } + + let mut new_segments = Vec::new_in(arena); + let mut last_text = String::new_in(arena); + + normalize_str_segments(arena, t, &mut last_text, &mut new_segments); + if !last_text.is_empty() { + new_segments.push(StrSegment::Plaintext(last_text.into_bump_str())); + } + + StrLiteral::Line(new_segments.into_bump_slice()) + } + StrLiteral::Block(t) => { + let mut new_segments = Vec::new_in(arena); + let mut last_text = String::new_in(arena); + for line in t { + normalize_str_segments(arena, line, &mut last_text, &mut new_segments); + } + if !last_text.is_empty() { + new_segments.push(StrSegment::Plaintext(last_text.into_bump_str())); + } + + StrLiteral::Line(new_segments.into_bump_slice()) + } } } } +fn normalize_str_segments<'a>( + arena: &'a Bump, + segments: &[StrSegment<'a>], + last_text: &mut String<'a>, + new_segments: &mut Vec<'a, StrSegment<'a>>, +) { + for segment in segments.iter() { + match segment { + StrSegment::Plaintext(t) => { + last_text.push_str(t); + } + StrSegment::Unicode(t) => { + let hex_code: &str = t.value; + let c = char::from_u32(u32::from_str_radix(hex_code, 16).unwrap()).unwrap(); + last_text.push(c); + } + StrSegment::EscapedChar(c) => { + last_text.push(c.unescape()); + } + StrSegment::Interpolated(e) => { + if !last_text.is_empty() { + let text = std::mem::replace(last_text, String::new_in(arena)); + new_segments.push(StrSegment::Plaintext(text.into_bump_str())); + } + new_segments.push(StrSegment::Interpolated(e.remove_spaces(arena))); + } + StrSegment::DeprecatedInterpolated(e) => { + if !last_text.is_empty() { + let text = std::mem::replace(last_text, String::new_in(arena)); + new_segments.push(StrSegment::Plaintext(text.into_bump_str())); + } + new_segments.push(StrSegment::Interpolated(e.remove_spaces(arena))); + } + } + } +} + +#[test] +fn test_str_normalize() { + use crate::test_helpers::parse_expr_with; + let arena = Bump::new(); + let a = parse_expr_with(&arena, r#""a\nb""#).unwrap(); + let b = parse_expr_with(&arena, "\"\"\"\na\nb\"\"\"").unwrap(); + + let ar = a.remove_spaces(&arena); + let br = b.remove_spaces(&arena); + + assert_eq!(ar, br); +} + impl<'a> RemoveSpaces<'a> for StrSegment<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { match *self { From 2d9aa007714f236567422d42ee89fbcd8af35a2f Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Mon, 12 Aug 2024 22:49:41 -0700 Subject: [PATCH 163/203] Rename RemoveSpaces->Normalize --- crates/cli/src/format.rs | 6 +- crates/compiler/parse/src/lib.rs | 2 +- .../src/{remove_spaces.rs => normalize.rs} | 1057 ++++++++--------- crates/compiler/test_syntax/src/minimize.rs | 15 +- .../compiler/test_syntax/src/test_helpers.rs | 18 +- crates/compiler/test_syntax/tests/test_fmt.rs | 6 +- 6 files changed, 527 insertions(+), 577 deletions(-) rename crates/compiler/parse/src/{remove_spaces.rs => normalize.rs} (63%) diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index c31958de651..6cd649a5ee2 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -9,7 +9,7 @@ use roc_fmt::header::fmt_header; use roc_fmt::Buf; use roc_parse::ast::{FullAst, SpacesBefore}; use roc_parse::header::parse_module_defs; -use roc_parse::remove_spaces::RemoveSpaces; +use roc_parse::normalize::Normalize; use roc_parse::{header, parser::SyntaxError, state::State}; #[derive(Copy, Clone, Debug)] @@ -200,8 +200,8 @@ pub fn format_src(arena: &Bump, src: &str) -> Result { } }; - let ast_normalized = ast.remove_spaces(arena); - let reparsed_ast_normalized = reparsed_ast.remove_spaces(arena); + let ast_normalized = ast.normalize(arena); + let reparsed_ast_normalized = reparsed_ast.normalize(arena); // HACK! // We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast, diff --git a/crates/compiler/parse/src/lib.rs b/crates/compiler/parse/src/lib.rs index 9651baf4030..f92899aa89a 100644 --- a/crates/compiler/parse/src/lib.rs +++ b/crates/compiler/parse/src/lib.rs @@ -13,10 +13,10 @@ pub mod header; pub mod highlight; pub mod ident; pub mod keyword; +pub mod normalize; pub mod number_literal; pub mod pattern; pub mod problems; -pub mod remove_spaces; pub mod src64; pub mod state; pub mod string_literal; diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/normalize.rs similarity index 63% rename from crates/compiler/parse/src/remove_spaces.rs rename to crates/compiler/parse/src/normalize.rs index 66e45e3cfd9..84ac34304ae 100644 --- a/crates/compiler/parse/src/remove_spaces.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -29,25 +29,26 @@ use crate::{ }, }; -/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting. +/// Normalizes the ast to something that we _expect_ to be invariant under formatting. /// /// Currently this consists of: /// * Removing newlines /// * Removing comments /// * Removing parens in Exprs +/// * Normalizing string encoding /// /// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting) /// - but there are currently several bugs where they're _not_ preserved. /// TODO: ensure formatting retains comments -pub trait RemoveSpaces<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self; +pub trait Normalize<'a> { + fn normalize(&self, arena: &'a Bump) -> Self; } macro_rules! keywords { ($($name:ident),* $(,)?) => { $( - impl<'a> RemoveSpaces<'a> for $name { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { + impl<'a> Normalize<'a> for $name { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } @@ -68,8 +69,8 @@ keywords! { PlatformKeyword, } -impl<'a> RemoveSpaces<'a> for Defs<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Defs<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { let mut defs = self.clone(); defs.spaces.clear(); @@ -77,202 +78,200 @@ impl<'a> RemoveSpaces<'a> for Defs<'a> { defs.space_after.clear(); for type_def in defs.type_defs.iter_mut() { - *type_def = type_def.remove_spaces(arena); + *type_def = type_def.normalize(arena); } for value_def in defs.value_defs.iter_mut() { - *value_def = value_def.remove_spaces(arena); + *value_def = value_def.normalize(arena); } for region_def in defs.regions.iter_mut() { - *region_def = region_def.remove_spaces(arena); + *region_def = region_def.normalize(arena); } defs } } -impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, V: Normalize<'a>> Normalize<'a> for Spaces<'a, V> { + fn normalize(&self, arena: &'a Bump) -> Self { Spaces { before: &[], - item: self.item.remove_spaces(arena), + item: self.item.normalize(arena), after: &[], } } } -impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for SpacesBefore<'a, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, V: Normalize<'a>> Normalize<'a> for SpacesBefore<'a, V> { + fn normalize(&self, arena: &'a Bump) -> Self { SpacesBefore { before: &[], - item: self.item.remove_spaces(arena), + item: self.item.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for FullAst<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for FullAst<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { FullAst { - header: self.header.remove_spaces(arena), - defs: self.defs.remove_spaces(arena), + header: self.header.normalize(arena), + defs: self.defs.normalize(arena), } } } -impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, K: Normalize<'a>, V: Normalize<'a>> Normalize<'a> for KeywordItem<'a, K, V> { + fn normalize(&self, arena: &'a Bump) -> Self { KeywordItem { - keyword: self.keyword.remove_spaces(arena), - item: self.item.remove_spaces(arena), + keyword: self.keyword.normalize(arena), + item: self.item.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ProvidesTo<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { ProvidesTo { - provides_keyword: self.provides_keyword.remove_spaces(arena), - entries: self.entries.remove_spaces(arena), - types: self.types.remove_spaces(arena), - to_keyword: self.to_keyword.remove_spaces(arena), - to: self.to.remove_spaces(arena), + provides_keyword: self.provides_keyword.normalize(arena), + entries: self.entries.normalize(arena), + types: self.types.normalize(arena), + to_keyword: self.to_keyword.normalize(arena), + to: self.to.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for Header<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Header<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { Header::Module(header) => Header::Module(ModuleHeader { after_keyword: &[], - params: header.params.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - interface_imports: header.interface_imports.remove_spaces(arena), + params: header.params.normalize(arena), + exposes: header.exposes.normalize(arena), + interface_imports: header.interface_imports.normalize(arena), }), Header::App(header) => Header::App(AppHeader { before_provides: &[], - provides: header.provides.remove_spaces(arena), + provides: header.provides.normalize(arena), before_packages: &[], - packages: header.packages.remove_spaces(arena), - old_imports: header.old_imports.remove_spaces(arena), - old_provides_to_new_package: header - .old_provides_to_new_package - .remove_spaces(arena), + packages: header.packages.normalize(arena), + old_imports: header.old_imports.normalize(arena), + old_provides_to_new_package: header.old_provides_to_new_package.normalize(arena), }), Header::Package(header) => Header::Package(PackageHeader { before_exposes: &[], - exposes: header.exposes.remove_spaces(arena), + exposes: header.exposes.normalize(arena), before_packages: &[], - packages: header.packages.remove_spaces(arena), + packages: header.packages.normalize(arena), }), Header::Platform(header) => Header::Platform(PlatformHeader { before_name: &[], - name: header.name.remove_spaces(arena), - requires: header.requires.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - packages: header.packages.remove_spaces(arena), - imports: header.imports.remove_spaces(arena), - provides: header.provides.remove_spaces(arena), + name: header.name.normalize(arena), + requires: header.requires.normalize(arena), + exposes: header.exposes.normalize(arena), + packages: header.packages.normalize(arena), + imports: header.imports.normalize(arena), + provides: header.provides.normalize(arena), }), Header::Hosted(header) => Header::Hosted(HostedHeader { before_name: &[], - name: header.name.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - imports: header.imports.remove_spaces(arena), - generates: header.generates.remove_spaces(arena), - generates_with: header.generates_with.remove_spaces(arena), + name: header.name.normalize(arena), + exposes: header.exposes.normalize(arena), + imports: header.imports.normalize(arena), + generates: header.generates.normalize(arena), + generates_with: header.generates_with.normalize(arena), }), } } } -impl<'a> RemoveSpaces<'a> for ModuleParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ModuleParams<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { ModuleParams { - params: self.params.remove_spaces(arena), + params: self.params.normalize(arena), before_arrow: &[], after_arrow: &[], } } } -impl<'a> RemoveSpaces<'a> for Region { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Region { + fn normalize(&self, _arena: &'a Bump) -> Self { Region::zero() } } -impl<'a> RemoveSpaces<'a> for &'a str { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for &'a str { + fn normalize(&self, _arena: &'a Bump) -> Self { self } } -impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, T: Normalize<'a> + Copy> Normalize<'a> for Spaced<'a, T> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { - Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)), - Spaced::SpaceBefore(a, _) => a.remove_spaces(arena), - Spaced::SpaceAfter(a, _) => a.remove_spaces(arena), + Spaced::Item(a) => Spaced::Item(a.normalize(arena)), + Spaced::SpaceBefore(a, _) => a.normalize(arena), + Spaced::SpaceAfter(a, _) => a.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for ExposedName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ExposedName<'a> { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for ModuleName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ModuleName<'a> { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for PackageName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PackageName<'a> { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for To<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for To<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { To::ExistingPackage(a) => To::ExistingPackage(a), - To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)), + To::NewPackage(a) => To::NewPackage(a.normalize(arena)), } } } -impl<'a> RemoveSpaces<'a> for TypedIdent<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for TypedIdent<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { TypedIdent { - ident: self.ident.remove_spaces(arena), + ident: self.ident.normalize(arena), spaces_before_colon: &[], - ann: self.ann.remove_spaces(arena), + ann: self.ann.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PlatformRequires<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { PlatformRequires { - rigids: self.rigids.remove_spaces(arena), - signature: self.signature.remove_spaces(arena), + rigids: self.rigids.normalize(arena), + signature: self.signature.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for UppercaseIdent<'a> { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for PackageEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PackageEntry<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { PackageEntry { shorthand: self.shorthand, spaces_after_shorthand: &[], @@ -280,83 +279,81 @@ impl<'a> RemoveSpaces<'a> for PackageEntry<'a> { Some(_) => Some(&[]), None => None, }, - package_name: self.package_name.remove_spaces(arena), + package_name: self.package_name.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImportsEntry<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { - ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)), - ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)), - ImportsEntry::IngestedFile(a, b) => { - ImportsEntry::IngestedFile(a, b.remove_spaces(arena)) - } + ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.normalize(arena)), + ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.normalize(arena)), + ImportsEntry::IngestedFile(a, b) => ImportsEntry::IngestedFile(a, b.normalize(arena)), } } } -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - self.as_ref().map(|a| a.remove_spaces(arena)) +impl<'a, T: Normalize<'a>> Normalize<'a> for Option { + fn normalize(&self, arena: &'a Bump) -> Self { + self.as_ref().map(|a| a.normalize(arena)) } } -impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let res = self.value.remove_spaces(arena); +impl<'a, T: Normalize<'a> + std::fmt::Debug> Normalize<'a> for Loc { + fn normalize(&self, arena: &'a Bump) -> Self { + let res = self.value.normalize(arena); Loc::at(Region::zero(), res) } } -impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - (self.0.remove_spaces(arena), self.1.remove_spaces(arena)) +impl<'a, A: Normalize<'a>, B: Normalize<'a>> Normalize<'a> for (A, B) { + fn normalize(&self, arena: &'a Bump) -> Self { + (self.0.normalize(arena), self.1.normalize(arena)) } } -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, T: Normalize<'a>> Normalize<'a> for Collection<'a, T> { + fn normalize(&self, arena: &'a Bump) -> Self { let mut items = Vec::with_capacity_in(self.items.len(), arena); for item in self.items { - items.push(item.remove_spaces(arena)); + items.push(item.normalize(arena)); } Collection::with_items(items.into_bump_slice()) } } -impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, T: Normalize<'a> + std::fmt::Debug> Normalize<'a> for &'a [T] { + fn normalize(&self, arena: &'a Bump) -> Self { let mut items = Vec::with_capacity_in(self.len(), arena); for item in *self { - let res = item.remove_spaces(arena); + let res = item.normalize(arena); items.push(res); } items.into_bump_slice() } } -impl<'a> RemoveSpaces<'a> for UnaryOp { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for UnaryOp { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for BinOp { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for BinOp { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - arena.alloc((*self).remove_spaces(arena)) +impl<'a, T: Normalize<'a>> Normalize<'a> for &'a T { + fn normalize(&self, arena: &'a Bump) -> Self { + arena.alloc((*self).normalize(arena)) } } -impl<'a> RemoveSpaces<'a> for TypeDef<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for TypeDef<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { use TypeDef::*; match *self { @@ -365,10 +362,10 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> { ann, } => Alias { header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), + name: name.normalize(arena), + vars: vars.normalize(arena), }, - ann: ann.remove_spaces(arena), + ann: ann.normalize(arena), }, Opaque { header: TypeHeader { name, vars }, @@ -376,11 +373,11 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> { derived, } => Opaque { header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), + name: name.normalize(arena), + vars: vars.normalize(arena), }, - typ: typ.remove_spaces(arena), - derived: derived.remove_spaces(arena), + typ: typ.normalize(arena), + derived: derived.normalize(arena), }, Ability { header: TypeHeader { name, vars }, @@ -388,25 +385,25 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> { members, } => Ability { header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), + name: name.normalize(arena), + vars: vars.normalize(arena), }, - loc_implements: loc_has.remove_spaces(arena), - members: members.remove_spaces(arena), + loc_implements: loc_has.normalize(arena), + members: members.normalize(arena), }, } } } -impl<'a> RemoveSpaces<'a> for ValueDef<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ValueDef<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { use ValueDef::*; match *self { - Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)), + Annotation(a, b) => Annotation(a.normalize(arena), b.normalize(arena)), Body(a, b) => Body( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), AnnotatedBody { ann_pattern, @@ -415,187 +412,187 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> { body_pattern, body_expr, } => AnnotatedBody { - ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), - ann_type: arena.alloc(ann_type.remove_spaces(arena)), + ann_pattern: arena.alloc(ann_pattern.normalize(arena)), + ann_type: arena.alloc(ann_type.normalize(arena)), lines_between: &[], - body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), - body_expr: arena.alloc(body_expr.remove_spaces(arena)), + body_pattern: arena.alloc(body_pattern.normalize(arena)), + body_expr: arena.alloc(body_expr.normalize(arena)), }, Dbg { condition, preceding_comment: _, } => Dbg { - condition: arena.alloc(condition.remove_spaces(arena)), + condition: arena.alloc(condition.normalize(arena)), preceding_comment: Region::zero(), }, Expect { condition, preceding_comment: _, } => Expect { - condition: arena.alloc(condition.remove_spaces(arena)), + condition: arena.alloc(condition.normalize(arena)), preceding_comment: Region::zero(), }, ExpectFx { condition, preceding_comment: _, } => ExpectFx { - condition: arena.alloc(condition.remove_spaces(arena)), + condition: arena.alloc(condition.normalize(arena)), preceding_comment: Region::zero(), }, - ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)), + ModuleImport(module_import) => ModuleImport(module_import.normalize(arena)), IngestedFileImport(ingested_file_import) => { - IngestedFileImport(ingested_file_import.remove_spaces(arena)) + IngestedFileImport(ingested_file_import.normalize(arena)) } - Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))), + Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.normalize(arena))), } } } -impl<'a> RemoveSpaces<'a> for ModuleImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ModuleImport<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { ModuleImport { before_name: &[], - name: self.name.remove_spaces(arena), - params: self.params.remove_spaces(arena), - alias: self.alias.remove_spaces(arena), - exposed: self.exposed.remove_spaces(arena), + name: self.name.normalize(arena), + params: self.params.normalize(arena), + alias: self.alias.normalize(arena), + exposed: self.exposed.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ModuleImportParams<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { ModuleImportParams { before: &[], - params: self.params.remove_spaces(arena), + params: self.params.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for IngestedFileImport<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { IngestedFileImport { before_path: &[], - path: self.path.remove_spaces(arena), - name: self.name.remove_spaces(arena), - annotation: self.annotation.remove_spaces(arena), + path: self.path.normalize(arena), + name: self.name.normalize(arena), + annotation: self.annotation.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImportedModuleName<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { ImportedModuleName { - package: self.package.remove_spaces(arena), - name: self.name.remove_spaces(arena), + package: self.package.normalize(arena), + name: self.name.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for ImportAlias<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImportAlias<'a> { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for ImportAsKeyword { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImportAsKeyword { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for ImportExposingKeyword { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImportExposingKeyword { + fn normalize(&self, _arena: &'a Bump) -> Self { *self } } -impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for IngestedFileAnnotation<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { IngestedFileAnnotation { before_colon: &[], - annotation: self.annotation.remove_spaces(arena), + annotation: self.annotation.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for Implements<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Implements<'a> { + fn normalize(&self, _arena: &'a Bump) -> Self { Implements::Implements } } -impl<'a> RemoveSpaces<'a> for AbilityMember<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for AbilityMember<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { AbilityMember { - name: self.name.remove_spaces(arena), - typ: self.typ.remove_spaces(arena), + name: self.name.normalize(arena), + typ: self.typ.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for WhenBranch<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for WhenBranch<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { WhenBranch { - patterns: self.patterns.remove_spaces(arena), - value: self.value.remove_spaces(arena), - guard: self.guard.remove_spaces(arena), + patterns: self.patterns.normalize(arena), + value: self.value.normalize(arena), + guard: self.guard.normalize(arena), } } } -impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a, T: Normalize<'a> + Copy + std::fmt::Debug> Normalize<'a> for AssignedField<'a, T> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue( - a.remove_spaces(arena), + a.normalize(arena), arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), + arena.alloc(c.normalize(arena)), ), AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue( - a.remove_spaces(arena), + a.normalize(arena), arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), + arena.alloc(c.normalize(arena)), ), AssignedField::IgnoredValue(a, _, c) => AssignedField::IgnoredValue( - a.remove_spaces(arena), + a.normalize(arena), arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), + arena.alloc(c.normalize(arena)), ), - AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)), + AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.normalize(arena)), AssignedField::Malformed(a) => AssignedField::Malformed(a), - AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena), - AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena), + AssignedField::SpaceBefore(a, _) => a.normalize(arena), + AssignedField::SpaceAfter(a, _) => a.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for OldRecordBuilderField<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value( - a.remove_spaces(arena), + a.normalize(arena), &[], - arena.alloc(c.remove_spaces(arena)), + arena.alloc(c.normalize(arena)), ), OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue( - a.remove_spaces(arena), + a.normalize(arena), &[], &[], - arena.alloc(c.remove_spaces(arena)), + arena.alloc(c.normalize(arena)), ), OldRecordBuilderField::LabelOnly(a) => { - OldRecordBuilderField::LabelOnly(a.remove_spaces(arena)) + OldRecordBuilderField::LabelOnly(a.normalize(arena)) } OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a), - OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena), - OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena), + OldRecordBuilderField::SpaceBefore(a, _) => a.normalize(arena), + OldRecordBuilderField::SpaceAfter(a, _) => a.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for StrLiteral<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t), StrLiteral::Line(t) => { @@ -616,7 +613,7 @@ impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { } if !needs_merge { - return StrLiteral::Line(t.remove_spaces(arena)); + return StrLiteral::Line(t.normalize(arena)); } let mut new_segments = Vec::new_in(arena); @@ -669,14 +666,14 @@ fn normalize_str_segments<'a>( let text = std::mem::replace(last_text, String::new_in(arena)); new_segments.push(StrSegment::Plaintext(text.into_bump_str())); } - new_segments.push(StrSegment::Interpolated(e.remove_spaces(arena))); + new_segments.push(StrSegment::Interpolated(e.normalize(arena))); } StrSegment::DeprecatedInterpolated(e) => { if !last_text.is_empty() { let text = std::mem::replace(last_text, String::new_in(arena)); new_segments.push(StrSegment::Plaintext(text.into_bump_str())); } - new_segments.push(StrSegment::Interpolated(e.remove_spaces(arena))); + new_segments.push(StrSegment::Interpolated(e.normalize(arena))); } } } @@ -689,28 +686,26 @@ fn test_str_normalize() { let a = parse_expr_with(&arena, r#""a\nb""#).unwrap(); let b = parse_expr_with(&arena, "\"\"\"\na\nb\"\"\"").unwrap(); - let ar = a.remove_spaces(&arena); - let br = b.remove_spaces(&arena); + let ar = a.normalize(&arena); + let br = b.normalize(&arena); assert_eq!(ar, br); } -impl<'a> RemoveSpaces<'a> for StrSegment<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for StrSegment<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { StrSegment::Plaintext(t) => StrSegment::Plaintext(t), - StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)), + StrSegment::Unicode(t) => StrSegment::Unicode(t.normalize(arena)), StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c), - StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)), - StrSegment::DeprecatedInterpolated(t) => { - StrSegment::Interpolated(t.remove_spaces(arena)) - } + StrSegment::Interpolated(t) => StrSegment::Interpolated(t.normalize(arena)), + StrSegment::DeprecatedInterpolated(t) => StrSegment::Interpolated(t.normalize(arena)), } } } -impl<'a> RemoveSpaces<'a> for Expr<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Expr<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { Expr::Float(a) => Expr::Float(a), Expr::Num(a) => Expr::Num(a), @@ -723,30 +718,30 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> { base, is_negative, }, - Expr::Str(a) => Expr::Str(a.remove_spaces(arena)), - Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b), + Expr::Str(a) => Expr::Str(a.normalize(arena)), + Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.normalize(arena)), b), Expr::AccessorFunction(a) => Expr::AccessorFunction(a), - Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b), - Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))), - Expr::List(a) => Expr::List(a.remove_spaces(arena)), + Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), + Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.normalize(arena))), + Expr::List(a) => Expr::List(a.normalize(arena)), Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { - update: arena.alloc(update.remove_spaces(arena)), - fields: fields.remove_spaces(arena), + update: arena.alloc(update.normalize(arena)), + fields: fields.normalize(arena), }, - Expr::Record(a) => Expr::Record(a.remove_spaces(arena)), - Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)), + Expr::Record(a) => Expr::Record(a.normalize(arena)), + Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.normalize(arena)), Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder { - mapper: arena.alloc(mapper.remove_spaces(arena)), - fields: fields.remove_spaces(arena), + mapper: arena.alloc(mapper.normalize(arena)), + fields: fields.normalize(arena), }, - Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)), + Expr::Tuple(a) => Expr::Tuple(a.normalize(arena)), Expr::Var { module_name, ident } => Expr::Var { module_name, ident }, Expr::Underscore(a) => Expr::Underscore(a), Expr::Tag(a) => Expr::Tag(a), Expr::OpaqueRef(a) => Expr::OpaqueRef(a), Expr::Closure(a, b) => Expr::Closure( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), Expr::Crash => Expr::Crash, Expr::Defs(a, b) => { @@ -757,74 +752,68 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> { defs.spaces.clear(); for type_def in defs.type_defs.iter_mut() { - *type_def = type_def.remove_spaces(arena); + *type_def = type_def.normalize(arena); } for value_def in defs.value_defs.iter_mut() { - *value_def = value_def.remove_spaces(arena); + *value_def = value_def.normalize(arena); } - Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena))) + Expr::Defs(arena.alloc(defs), arena.alloc(b.normalize(arena))) } Expr::Backpassing(a, b, c) => Expr::Backpassing( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - arena.alloc(c.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), + arena.alloc(c.normalize(arena)), ), Expr::Expect(a, b) => Expr::Expect( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), Expr::Dbg(a, b) => Expr::Dbg( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), Expr::LowLevelDbg(x, a, b) => Expr::LowLevelDbg( x, - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::Apply(a, b, c) => Expr::Apply( - arena.alloc(a.remove_spaces(arena)), - b.remove_spaces(arena), - c, + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), - Expr::BinOps(a, b) => { - Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))) + Expr::Apply(a, b, c) => { + Expr::Apply(arena.alloc(a.normalize(arena)), b.normalize(arena), c) } + Expr::BinOps(a, b) => Expr::BinOps(a.normalize(arena), arena.alloc(b.normalize(arena))), Expr::UnaryOp(a, b) => { - Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) - } - Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))), - Expr::When(a, b) => { - Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) + Expr::UnaryOp(arena.alloc(a.normalize(arena)), b.normalize(arena)) } + Expr::If(a, b) => Expr::If(a.normalize(arena), arena.alloc(b.normalize(arena))), + Expr::When(a, b) => Expr::When(arena.alloc(a.normalize(arena)), b.normalize(arena)), Expr::ParensAround(a) => { // The formatter can remove redundant parentheses, so also remove these when normalizing for comparison. - a.remove_spaces(arena) + a.normalize(arena) } Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), Expr::MalformedClosure => Expr::MalformedClosure, Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), - Expr::SpaceBefore(a, _) => a.remove_spaces(arena), - Expr::SpaceAfter(a, _) => a.remove_spaces(arena), + Expr::SpaceBefore(a, _) => a.normalize(arena), + Expr::SpaceAfter(a, _) => a.normalize(arena), Expr::SingleQuote(a) => Expr::Num(a), Expr::MultipleOldRecordBuilders(a) => { - Expr::MultipleOldRecordBuilders(arena.alloc(a.remove_spaces(arena))) + Expr::MultipleOldRecordBuilders(arena.alloc(a.normalize(arena))) } Expr::UnappliedOldRecordBuilder(a) => { - Expr::UnappliedOldRecordBuilder(arena.alloc(a.remove_spaces(arena))) + Expr::UnappliedOldRecordBuilder(arena.alloc(a.normalize(arena))) } Expr::EmptyRecordBuilder(a) => { - Expr::EmptyRecordBuilder(arena.alloc(a.remove_spaces(arena))) + Expr::EmptyRecordBuilder(arena.alloc(a.normalize(arena))) } Expr::SingleFieldRecordBuilder(a) => { - Expr::SingleFieldRecordBuilder(arena.alloc(a.remove_spaces(arena))) + Expr::SingleFieldRecordBuilder(arena.alloc(a.normalize(arena))) } Expr::OptionalFieldInRecordBuilder(a, b) => Expr::OptionalFieldInRecordBuilder( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), } } @@ -853,26 +842,26 @@ fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent { } } -impl<'a> RemoveSpaces<'a> for Pattern<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Pattern<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { Pattern::Identifier { ident } => Pattern::Identifier { ident }, Pattern::Tag(a) => Pattern::Tag(a), Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), Pattern::Apply(a, b) => Pattern::Apply( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), - Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)), + Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.normalize(arena)), Pattern::RequiredField(a, b) => { - Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena))) + Pattern::RequiredField(a, arena.alloc(b.normalize(arena))) } Pattern::OptionalField(a, b) => { - Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena))) + Pattern::OptionalField(a, arena.alloc(b.normalize(arena))) } Pattern::As(pattern, pattern_as) => Pattern::As( - arena.alloc(pattern.remove_spaces(arena)), - pattern_as.remove_spaces(arena), + arena.alloc(pattern.normalize(arena)), + pattern_as.normalize(arena), ), Pattern::NumLiteral(a) => Pattern::NumLiteral(a), Pattern::NonBase10Literal { @@ -892,136 +881,133 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> { Pattern::QualifiedIdentifier { module_name, ident } => { Pattern::QualifiedIdentifier { module_name, ident } } - Pattern::SpaceBefore(a, _) => a.remove_spaces(arena), - Pattern::SpaceAfter(a, _) => a.remove_spaces(arena), + Pattern::SpaceBefore(a, _) => a.normalize(arena), + Pattern::SpaceAfter(a, _) => a.normalize(arena), Pattern::SingleQuote(a) => Pattern::SingleQuote(a), - Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)), - Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)), + Pattern::List(pats) => Pattern::List(pats.normalize(arena)), + Pattern::Tuple(pats) => Pattern::Tuple(pats.normalize(arena)), Pattern::ListRest(opt_pattern_as) => Pattern::ListRest( - opt_pattern_as - .map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))), + opt_pattern_as.map(|(_, pattern_as)| ([].as_ref(), pattern_as.normalize(arena))), ), } } } -impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for TypeAnnotation<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { TypeAnnotation::Function(a, b) => TypeAnnotation::Function( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), + arena.alloc(b.normalize(arena)), ), - TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)), + TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.normalize(arena)), TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a), TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As( - arena.alloc(a.remove_spaces(arena)), + arena.alloc(a.normalize(arena)), &[], TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), + name: name.normalize(arena), + vars: vars.normalize(arena), }, ), TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple { - elems: fields.remove_spaces(arena), - ext: ext.remove_spaces(arena), + elems: fields.normalize(arena), + ext: ext.normalize(arena), }, TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record { - fields: fields.remove_spaces(arena), - ext: ext.remove_spaces(arena), + fields: fields.normalize(arena), + ext: ext.normalize(arena), }, TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion { - ext: ext.remove_spaces(arena), - tags: tags.remove_spaces(arena), + ext: ext.normalize(arena), + tags: tags.normalize(arena), }, TypeAnnotation::Inferred => TypeAnnotation::Inferred, TypeAnnotation::Wildcard => TypeAnnotation::Wildcard, TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where( - arena.alloc(annot.remove_spaces(arena)), - arena.alloc(has_clauses.remove_spaces(arena)), + arena.alloc(annot.normalize(arena)), + arena.alloc(has_clauses.normalize(arena)), ), - TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena), - TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena), + TypeAnnotation::SpaceBefore(a, _) => a.normalize(arena), + TypeAnnotation::SpaceAfter(a, _) => a.normalize(arena), TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a), } } } -impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImplementsClause<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { ImplementsClause { - var: self.var.remove_spaces(arena), - abilities: self.abilities.remove_spaces(arena), + var: self.var.normalize(arena), + abilities: self.abilities.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for Tag<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Tag<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { Tag::Apply { name, args } => Tag::Apply { - name: name.remove_spaces(arena), - args: args.remove_spaces(arena), + name: name.normalize(arena), + args: args.normalize(arena), }, Tag::Malformed(a) => Tag::Malformed(a), - Tag::SpaceBefore(a, _) => a.remove_spaces(arena), - Tag::SpaceAfter(a, _) => a.remove_spaces(arena), + Tag::SpaceBefore(a, _) => a.normalize(arena), + Tag::SpaceAfter(a, _) => a.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for AbilityImpls<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { - AbilityImpls::AbilityImpls(impls) => { - AbilityImpls::AbilityImpls(impls.remove_spaces(arena)) - } + AbilityImpls::AbilityImpls(impls) => AbilityImpls::AbilityImpls(impls.normalize(arena)), AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => { - has.remove_spaces(arena) + has.normalize(arena) } } } } -impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImplementsAbility<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { ImplementsAbility::ImplementsAbility { ability, impls } => { ImplementsAbility::ImplementsAbility { - ability: ability.remove_spaces(arena), - impls: impls.remove_spaces(arena), + ability: ability.normalize(arena), + impls: impls.normalize(arena), } } ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => { - has.remove_spaces(arena) + has.normalize(arena) } } } } -impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ImplementsAbilities<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match *self { ImplementsAbilities::Implements(derived) => { - ImplementsAbilities::Implements(derived.remove_spaces(arena)) + ImplementsAbilities::Implements(derived.normalize(arena)) } ImplementsAbilities::SpaceBefore(derived, _) - | ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena), + | ImplementsAbilities::SpaceAfter(derived, _) => derived.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for PatternAs<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PatternAs<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { PatternAs { spaces_before: &[], - identifier: self.identifier.remove_spaces(arena), + identifier: self.identifier.normalize(arena), } } } -impl<'a> RemoveSpaces<'a> for EExpr<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EExpr<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EExpr::TrailingOperator(_pos) => EExpr::TrailingOperator(Position::zero()), EExpr::Start(_pos) => EExpr::Start(Position::zero()), @@ -1032,24 +1018,22 @@ impl<'a> RemoveSpaces<'a> for EExpr<'a> { EExpr::Access(_pos) => EExpr::Access(Position::zero()), EExpr::UnaryNot(_pos) => EExpr::UnaryNot(Position::zero()), EExpr::UnaryNegate(_pos) => EExpr::UnaryNegate(Position::zero()), - EExpr::BadOperator(inner_err, _pos) => EExpr::BadOperator( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + EExpr::BadOperator(inner_err, _pos) => { + EExpr::BadOperator(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } EExpr::DefMissingFinalExpr(_pos) => EExpr::DefMissingFinalExpr(Position::zero()), EExpr::DefMissingFinalExpr2(inner_err, _pos) => EExpr::DefMissingFinalExpr2( - arena.alloc(inner_err.remove_spaces(arena)), + arena.alloc(inner_err.normalize(arena)), Position::zero(), ), EExpr::Type(inner_err, _pos) => { - EExpr::Type(inner_err.remove_spaces(arena), Position::zero()) + EExpr::Type(inner_err.normalize(arena), Position::zero()) + } + EExpr::Pattern(inner_err, _pos) => { + EExpr::Pattern(arena.alloc(inner_err.normalize(arena)), Position::zero()) } - EExpr::Pattern(inner_err, _pos) => EExpr::Pattern( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), EExpr::Ability(inner_err, _pos) => { - EExpr::Ability(inner_err.remove_spaces(arena), Position::zero()) + EExpr::Ability(inner_err.normalize(arena), Position::zero()) } EExpr::IndentDefBody(_pos) => EExpr::IndentDefBody(Position::zero()), EExpr::IndentEquals(_pos) => EExpr::IndentEquals(Position::zero()), @@ -1068,30 +1052,26 @@ impl<'a> RemoveSpaces<'a> for EExpr<'a> { EExpr::BackpassContinue(_pos) => EExpr::BackpassContinue(Position::zero()), EExpr::DbgContinue(_pos) => EExpr::DbgContinue(Position::zero()), EExpr::When(inner_err, _pos) => { - EExpr::When(inner_err.remove_spaces(arena), Position::zero()) - } - EExpr::If(inner_err, _pos) => { - EExpr::If(inner_err.remove_spaces(arena), Position::zero()) + EExpr::When(inner_err.normalize(arena), Position::zero()) } + EExpr::If(inner_err, _pos) => EExpr::If(inner_err.normalize(arena), Position::zero()), EExpr::Expect(inner_err, _pos) => { - EExpr::Expect(inner_err.remove_spaces(arena), Position::zero()) - } - EExpr::Dbg(inner_err, _pos) => { - EExpr::Dbg(inner_err.remove_spaces(arena), Position::zero()) + EExpr::Expect(inner_err.normalize(arena), Position::zero()) } + EExpr::Dbg(inner_err, _pos) => EExpr::Dbg(inner_err.normalize(arena), Position::zero()), EExpr::Import(inner_err, _pos) => { - EExpr::Import(inner_err.remove_spaces(arena), Position::zero()) + EExpr::Import(inner_err.normalize(arena), Position::zero()) } EExpr::Closure(inner_err, _pos) => { - EExpr::Closure(inner_err.remove_spaces(arena), Position::zero()) + EExpr::Closure(inner_err.normalize(arena), Position::zero()) } EExpr::Underscore(_pos) => EExpr::Underscore(Position::zero()), EExpr::Crash(_pos) => EExpr::Crash(Position::zero()), EExpr::InParens(inner_err, _pos) => { - EExpr::InParens(inner_err.remove_spaces(arena), Position::zero()) + EExpr::InParens(inner_err.normalize(arena), Position::zero()) } EExpr::Record(inner_err, _pos) => { - EExpr::Record(inner_err.remove_spaces(arena), Position::zero()) + EExpr::Record(inner_err.normalize(arena), Position::zero()) } EExpr::OptionalValueInOldRecordBuilder(_pos) => { EExpr::OptionalValueInOldRecordBuilder(Region::zero()) @@ -1099,12 +1079,10 @@ impl<'a> RemoveSpaces<'a> for EExpr<'a> { EExpr::IgnoredValueInOldRecordBuilder(_pos) => { EExpr::OptionalValueInOldRecordBuilder(Region::zero()) } - EExpr::Str(inner_err, _pos) => { - EExpr::Str(inner_err.remove_spaces(arena), Position::zero()) - } + EExpr::Str(inner_err, _pos) => EExpr::Str(inner_err.normalize(arena), Position::zero()), EExpr::Number(inner_err, _pos) => EExpr::Number(inner_err.clone(), Position::zero()), EExpr::List(inner_err, _pos) => { - EExpr::List(inner_err.remove_spaces(arena), Position::zero()) + EExpr::List(inner_err.normalize(arena), Position::zero()) } EExpr::IndentStart(_pos) => EExpr::IndentStart(Position::zero()), EExpr::IndentEnd(_pos) => EExpr::IndentEnd(Position::zero()), @@ -1124,22 +1102,21 @@ impl<'a> RemoveSpaces<'a> for EExpr<'a> { } } -impl<'a> RemoveSpaces<'a> for EList<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EList<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EList::Open(_pos) => EList::Open(Position::zero()), EList::End(_pos) => EList::End(Position::zero()), EList::Space(inner_err, _pos) => EList::Space(*inner_err, Position::zero()), - EList::Expr(inner_err, _pos) => EList::Expr( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + EList::Expr(inner_err, _pos) => { + EList::Expr(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } } } } -impl<'a> RemoveSpaces<'a> for EString<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EString<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EString::Open(_) => EString::Open(Position::zero()), EString::CodePtOpen(_) => EString::CodePtOpen(Position::zero()), @@ -1153,7 +1130,7 @@ impl<'a> RemoveSpaces<'a> for EString<'a> { EString::EndlessSingleQuote(_) => EString::EndlessSingleQuote(Position::zero()), EString::UnknownEscape(_) => EString::UnknownEscape(Position::zero()), EString::Format(inner, _) => { - EString::Format(arena.alloc(inner.remove_spaces(arena)), Position::zero()) + EString::Format(arena.alloc(inner.normalize(arena)), Position::zero()) } EString::FormatEnd(_) => EString::FormatEnd(Position::zero()), EString::MultilineInsufficientIndent(_) => { @@ -1166,8 +1143,8 @@ impl<'a> RemoveSpaces<'a> for EString<'a> { } } -impl<'a> RemoveSpaces<'a> for EClosure<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EClosure<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EClosure::Space(inner_err, _) => EClosure::Space(*inner_err, Position::zero()), EClosure::Start(_) => EClosure::Start(Position::zero()), @@ -1175,12 +1152,11 @@ impl<'a> RemoveSpaces<'a> for EClosure<'a> { EClosure::Comma(_) => EClosure::Comma(Position::zero()), EClosure::Arg(_) => EClosure::Arg(Position::zero()), EClosure::Pattern(inner_err, _) => { - EClosure::Pattern(inner_err.remove_spaces(arena), Position::zero()) + EClosure::Pattern(inner_err.normalize(arena), Position::zero()) + } + EClosure::Body(inner_err, _) => { + EClosure::Body(arena.alloc(inner_err.normalize(arena)), Position::zero()) } - EClosure::Body(inner_err, _) => EClosure::Body( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()), EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()), EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()), @@ -1188,23 +1164,22 @@ impl<'a> RemoveSpaces<'a> for EClosure<'a> { } } -impl<'a> RemoveSpaces<'a> for EInParens<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EInParens<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EInParens::End(_) => EInParens::End(Position::zero()), EInParens::Open(_) => EInParens::Open(Position::zero()), EInParens::Empty(_) => EInParens::Empty(Position::zero()), - EInParens::Expr(inner_err, _) => EInParens::Expr( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + EInParens::Expr(inner_err, _) => { + EInParens::Expr(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } EInParens::Space(inner_err, _) => EInParens::Space(*inner_err, Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for ERecord<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ERecord<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ERecord::End(_) => ERecord::End(Position::zero()), ERecord::Open(_) => ERecord::Open(Position::zero()), @@ -1214,24 +1189,23 @@ impl<'a> RemoveSpaces<'a> for ERecord<'a> { ERecord::QuestionMark(_) => ERecord::QuestionMark(Position::zero()), ERecord::Arrow(_) => ERecord::Arrow(Position::zero()), ERecord::Ampersand(_) => ERecord::Ampersand(Position::zero()), - ERecord::Expr(inner_err, _) => ERecord::Expr( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + ERecord::Expr(inner_err, _) => { + ERecord::Expr(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } ERecord::Space(inner_err, _) => ERecord::Space(*inner_err, Position::zero()), ERecord::Prefix(_) => ERecord::Prefix(Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for EPattern<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EPattern<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EPattern::Record(inner_err, _) => { - EPattern::Record(inner_err.remove_spaces(arena), Position::zero()) + EPattern::Record(inner_err.normalize(arena), Position::zero()) } EPattern::List(inner_err, _) => { - EPattern::List(inner_err.remove_spaces(arena), Position::zero()) + EPattern::List(inner_err.normalize(arena), Position::zero()) } EPattern::AsKeyword(_) => EPattern::AsKeyword(Position::zero()), EPattern::AsIdentifier(_) => EPattern::AsIdentifier(Position::zero()), @@ -1241,7 +1215,7 @@ impl<'a> RemoveSpaces<'a> for EPattern<'a> { EPattern::End(_) => EPattern::End(Position::zero()), EPattern::Space(inner_err, _) => EPattern::Space(*inner_err, Position::zero()), EPattern::PInParens(inner_err, _) => { - EPattern::PInParens(inner_err.remove_spaces(arena), Position::zero()) + EPattern::PInParens(inner_err.normalize(arena), Position::zero()) } EPattern::NumLiteral(inner_err, _) => { EPattern::NumLiteral(inner_err.clone(), Position::zero()) @@ -1254,8 +1228,8 @@ impl<'a> RemoveSpaces<'a> for EPattern<'a> { } } -impl<'a> RemoveSpaces<'a> for EImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EImport<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EImport::Import(_) => EImport::Import(Position::zero()), EImport::IndentStart(_) => EImport::IndentStart(Position::zero()), @@ -1263,7 +1237,7 @@ impl<'a> RemoveSpaces<'a> for EImport<'a> { EImport::PackageShorthandDot(_) => EImport::PackageShorthandDot(Position::zero()), EImport::ModuleName(_) => EImport::ModuleName(Position::zero()), EImport::Params(inner_err, _) => { - EImport::Params(inner_err.remove_spaces(arena), Position::zero()) + EImport::Params(inner_err.normalize(arena), Position::zero()) } EImport::IndentAs(_) => EImport::IndentAs(Position::zero()), EImport::As(_) => EImport::As(Position::zero()), @@ -1283,7 +1257,7 @@ impl<'a> RemoveSpaces<'a> for EImport<'a> { EImport::Colon(_) => EImport::Colon(Position::zero()), EImport::IndentAnnotation(_) => EImport::IndentAnnotation(Position::zero()), EImport::Annotation(inner_err, _) => { - EImport::Annotation(inner_err.remove_spaces(arena), Position::zero()) + EImport::Annotation(inner_err.normalize(arena), Position::zero()) } EImport::Space(inner_err, _) => EImport::Space(*inner_err, Position::zero()), EImport::EndNewline(_) => EImport::EndNewline(Position::zero()), @@ -1291,25 +1265,25 @@ impl<'a> RemoveSpaces<'a> for EImport<'a> { } } -impl<'a> RemoveSpaces<'a> for EType<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EType<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EType::Space(inner_err, _) => EType::Space(*inner_err, Position::zero()), EType::UnderscoreSpacing(_) => EType::UnderscoreSpacing(Position::zero()), EType::TRecord(inner_err, _) => { - EType::TRecord(inner_err.remove_spaces(arena), Position::zero()) + EType::TRecord(inner_err.normalize(arena), Position::zero()) } EType::TTagUnion(inner_err, _) => { - EType::TTagUnion(inner_err.remove_spaces(arena), Position::zero()) + EType::TTagUnion(inner_err.normalize(arena), Position::zero()) } EType::TInParens(inner_err, _) => { - EType::TInParens(inner_err.remove_spaces(arena), Position::zero()) + EType::TInParens(inner_err.normalize(arena), Position::zero()) } EType::TApply(inner_err, _) => { - EType::TApply(inner_err.remove_spaces(arena), Position::zero()) + EType::TApply(inner_err.normalize(arena), Position::zero()) } EType::TInlineAlias(inner_err, _) => { - EType::TInlineAlias(inner_err.remove_spaces(arena), Position::zero()) + EType::TInlineAlias(inner_err.normalize(arena), Position::zero()) } EType::TBadTypeVariable(_) => EType::TBadTypeVariable(Position::zero()), EType::TWildcard(_) => EType::TWildcard(Position::zero()), @@ -1320,7 +1294,7 @@ impl<'a> RemoveSpaces<'a> for EType<'a> { EType::TWhereBar(_) => EType::TWhereBar(Position::zero()), EType::TImplementsClause(_) => EType::TImplementsClause(Position::zero()), EType::TAbilityImpl(inner_err, _) => { - EType::TAbilityImpl(inner_err.remove_spaces(arena), Position::zero()) + EType::TAbilityImpl(inner_err.normalize(arena), Position::zero()) } EType::TIndentStart(_) => EType::TIndentStart(Position::zero()), EType::TIndentEnd(_) => EType::TIndentEnd(Position::zero()), @@ -1329,12 +1303,12 @@ impl<'a> RemoveSpaces<'a> for EType<'a> { } } -impl<'a> RemoveSpaces<'a> for EImportParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EImportParams<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EImportParams::Indent(_) => EImportParams::Indent(Position::zero()), EImportParams::Record(inner_err, _) => { - EImportParams::Record(inner_err.remove_spaces(arena), Position::zero()) + EImportParams::Record(inner_err.normalize(arena), Position::zero()) } EImportParams::RecordUpdateFound(_) => EImportParams::RecordUpdateFound(Region::zero()), EImportParams::RecordApplyFound(_) => EImportParams::RecordApplyFound(Region::zero()), @@ -1351,23 +1325,22 @@ impl<'a> RemoveSpaces<'a> for EImportParams<'a> { } } -impl<'a> RemoveSpaces<'a> for PInParens<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PInParens<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { PInParens::Empty(_) => PInParens::Empty(Position::zero()), PInParens::End(_) => PInParens::End(Position::zero()), PInParens::Open(_) => PInParens::Open(Position::zero()), - PInParens::Pattern(inner_err, _) => PInParens::Pattern( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + PInParens::Pattern(inner_err, _) => { + PInParens::Pattern(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } PInParens::Space(inner_err, _) => PInParens::Space(*inner_err, Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for ETypeAbilityImpl<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypeAbilityImpl<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ETypeAbilityImpl::End(_) => ETypeAbilityImpl::End(Position::zero()), ETypeAbilityImpl::Open(_) => ETypeAbilityImpl::Open(Position::zero()), @@ -1378,19 +1351,17 @@ impl<'a> RemoveSpaces<'a> for ETypeAbilityImpl<'a> { ETypeAbilityImpl::Colon(_) => ETypeAbilityImpl::Colon(Position::zero()), ETypeAbilityImpl::Arrow(_) => ETypeAbilityImpl::Arrow(Position::zero()), ETypeAbilityImpl::Optional(_) => ETypeAbilityImpl::Optional(Position::zero()), - ETypeAbilityImpl::Type(inner_err, _) => ETypeAbilityImpl::Type( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + ETypeAbilityImpl::Type(inner_err, _) => { + ETypeAbilityImpl::Type(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } ETypeAbilityImpl::Space(inner_err, _) => { ETypeAbilityImpl::Space(*inner_err, Position::zero()) } ETypeAbilityImpl::QuestionMark(_) => ETypeAbilityImpl::QuestionMark(Position::zero()), ETypeAbilityImpl::Ampersand(_) => ETypeAbilityImpl::Ampersand(Position::zero()), - ETypeAbilityImpl::Expr(inner_err, _) => ETypeAbilityImpl::Expr( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + ETypeAbilityImpl::Expr(inner_err, _) => { + ETypeAbilityImpl::Expr(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } ETypeAbilityImpl::IndentBar(_) => ETypeAbilityImpl::IndentBar(Position::zero()), ETypeAbilityImpl::IndentAmpersand(_) => { ETypeAbilityImpl::IndentAmpersand(Position::zero()) @@ -1400,8 +1371,8 @@ impl<'a> RemoveSpaces<'a> for ETypeAbilityImpl<'a> { } } -impl<'a> RemoveSpaces<'a> for ETypeInlineAlias { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypeInlineAlias { + fn normalize(&self, _arena: &'a Bump) -> Self { match self { ETypeInlineAlias::NotAnAlias(_pos) => ETypeInlineAlias::NotAnAlias(Position::zero()), ETypeInlineAlias::Qualified(_pos) => ETypeInlineAlias::Qualified(Position::zero()), @@ -1412,8 +1383,8 @@ impl<'a> RemoveSpaces<'a> for ETypeInlineAlias { } } -impl<'a> RemoveSpaces<'a> for ETypeApply { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypeApply { + fn normalize(&self, _arena: &'a Bump) -> Self { match self { ETypeApply::StartNotUppercase(_) => ETypeApply::StartNotUppercase(Position::zero()), ETypeApply::End(_) => ETypeApply::End(Position::zero()), @@ -1425,16 +1396,15 @@ impl<'a> RemoveSpaces<'a> for ETypeApply { } } -impl<'a> RemoveSpaces<'a> for ETypeInParens<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypeInParens<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ETypeInParens::Empty(_) => ETypeInParens::Empty(Position::zero()), ETypeInParens::End(_) => ETypeInParens::End(Position::zero()), ETypeInParens::Open(_) => ETypeInParens::Open(Position::zero()), - ETypeInParens::Type(inner_err, _) => ETypeInParens::Type( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + ETypeInParens::Type(inner_err, _) => { + ETypeInParens::Type(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } ETypeInParens::Space(inner_err, _) => { ETypeInParens::Space(*inner_err, Position::zero()) } @@ -1444,15 +1414,14 @@ impl<'a> RemoveSpaces<'a> for ETypeInParens<'a> { } } -impl<'a> RemoveSpaces<'a> for ETypeTagUnion<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypeTagUnion<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ETypeTagUnion::End(_) => ETypeTagUnion::End(Position::zero()), ETypeTagUnion::Open(_) => ETypeTagUnion::Open(Position::zero()), - ETypeTagUnion::Type(inner_err, _) => ETypeTagUnion::Type( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + ETypeTagUnion::Type(inner_err, _) => { + ETypeTagUnion::Type(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } ETypeTagUnion::Space(inner_err, _) => { ETypeTagUnion::Space(*inner_err, Position::zero()) } @@ -1460,18 +1429,17 @@ impl<'a> RemoveSpaces<'a> for ETypeTagUnion<'a> { } } -impl<'a> RemoveSpaces<'a> for ETypeRecord<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypeRecord<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ETypeRecord::End(_) => ETypeRecord::End(Position::zero()), ETypeRecord::Open(_) => ETypeRecord::Open(Position::zero()), ETypeRecord::Field(_) => ETypeRecord::Field(Position::zero()), ETypeRecord::Colon(_) => ETypeRecord::Colon(Position::zero()), ETypeRecord::Optional(_) => ETypeRecord::Optional(Position::zero()), - ETypeRecord::Type(inner_err, _) => ETypeRecord::Type( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + ETypeRecord::Type(inner_err, _) => { + ETypeRecord::Type(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } ETypeRecord::Space(inner_err, _) => ETypeRecord::Space(*inner_err, Position::zero()), ETypeRecord::IndentOpen(_) => ETypeRecord::IndentOpen(Position::zero()), ETypeRecord::IndentColon(_) => ETypeRecord::IndentColon(Position::zero()), @@ -1481,79 +1449,71 @@ impl<'a> RemoveSpaces<'a> for ETypeRecord<'a> { } } -impl<'a> RemoveSpaces<'a> for PRecord<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PRecord<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { PRecord::End(_) => PRecord::End(Position::zero()), PRecord::Open(_) => PRecord::Open(Position::zero()), PRecord::Field(_) => PRecord::Field(Position::zero()), PRecord::Colon(_) => PRecord::Colon(Position::zero()), PRecord::Optional(_) => PRecord::Optional(Position::zero()), - PRecord::Pattern(inner_err, _) => PRecord::Pattern( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), - PRecord::Expr(inner_err, _) => PRecord::Expr( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + PRecord::Pattern(inner_err, _) => { + PRecord::Pattern(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } + PRecord::Expr(inner_err, _) => { + PRecord::Expr(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } PRecord::Space(inner_err, _) => PRecord::Space(*inner_err, Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for PList<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for PList<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { PList::End(_) => PList::End(Position::zero()), PList::Open(_) => PList::Open(Position::zero()), PList::Rest(_) => PList::Rest(Position::zero()), - PList::Pattern(inner_err, _) => PList::Pattern( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + PList::Pattern(inner_err, _) => { + PList::Pattern(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } PList::Space(inner_err, _) => PList::Space(*inner_err, Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for EExpect<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EExpect<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EExpect::Space(inner_err, _) => EExpect::Space(*inner_err, Position::zero()), EExpect::Dbg(_) => EExpect::Dbg(Position::zero()), EExpect::Expect(_) => EExpect::Expect(Position::zero()), - EExpect::Condition(inner_err, _) => EExpect::Condition( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), - EExpect::Continuation(inner_err, _) => EExpect::Continuation( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + EExpect::Condition(inner_err, _) => { + EExpect::Condition(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } + EExpect::Continuation(inner_err, _) => { + EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for EIf<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EIf<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EIf::Space(inner_err, _) => EIf::Space(*inner_err, Position::zero()), EIf::If(_) => EIf::If(Position::zero()), EIf::Then(_) => EIf::Then(Position::zero()), EIf::Else(_) => EIf::Else(Position::zero()), - EIf::Condition(inner_err, _) => EIf::Condition( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), - EIf::ThenBranch(inner_err, _) => EIf::ThenBranch( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), - EIf::ElseBranch(inner_err, _) => EIf::ElseBranch( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + EIf::Condition(inner_err, _) => { + EIf::Condition(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } + EIf::ThenBranch(inner_err, _) => { + EIf::ThenBranch(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } + EIf::ElseBranch(inner_err, _) => { + EIf::ElseBranch(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } EIf::IndentCondition(_) => EIf::IndentCondition(Position::zero()), EIf::IndentIf(_) => EIf::IndentIf(Position::zero()), EIf::IndentThenToken(_) => EIf::IndentThenToken(Position::zero()), @@ -1564,30 +1524,27 @@ impl<'a> RemoveSpaces<'a> for EIf<'a> { } } -impl<'a> RemoveSpaces<'a> for EWhen<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EWhen<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EWhen::Space(inner_err, _) => EWhen::Space(*inner_err, Position::zero()), EWhen::When(_) => EWhen::When(Position::zero()), EWhen::Is(_) => EWhen::Is(Position::zero()), EWhen::Pattern(inner_err, _) => { - EWhen::Pattern(inner_err.remove_spaces(arena), Position::zero()) + EWhen::Pattern(inner_err.normalize(arena), Position::zero()) } EWhen::Arrow(_) => EWhen::Arrow(Position::zero()), EWhen::Bar(_) => EWhen::Bar(Position::zero()), EWhen::IfToken(_) => EWhen::IfToken(Position::zero()), - EWhen::IfGuard(inner_err, _) => EWhen::IfGuard( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), - EWhen::Condition(inner_err, _) => EWhen::Condition( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), - EWhen::Branch(inner_err, _) => EWhen::Branch( - arena.alloc(inner_err.remove_spaces(arena)), - Position::zero(), - ), + EWhen::IfGuard(inner_err, _) => { + EWhen::IfGuard(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } + EWhen::Condition(inner_err, _) => { + EWhen::Condition(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } + EWhen::Branch(inner_err, _) => { + EWhen::Branch(arena.alloc(inner_err.normalize(arena)), Position::zero()) + } EWhen::IndentCondition(_) => EWhen::IndentCondition(Position::zero()), EWhen::IndentPattern(_) => EWhen::IndentPattern(Position::zero()), EWhen::IndentArrow(_) => EWhen::IndentArrow(Position::zero()), @@ -1598,12 +1555,12 @@ impl<'a> RemoveSpaces<'a> for EWhen<'a> { } } -impl<'a> RemoveSpaces<'a> for EAbility<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EAbility<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EAbility::Space(inner_err, _) => EAbility::Space(*inner_err, Position::zero()), EAbility::Type(inner_err, _) => { - EAbility::Type(inner_err.remove_spaces(arena), Position::zero()) + EAbility::Type(inner_err.normalize(arena), Position::zero()) } EAbility::DemandAlignment(_alignment, _) => { EAbility::DemandAlignment(0, Position::zero()) @@ -1614,8 +1571,8 @@ impl<'a> RemoveSpaces<'a> for EAbility<'a> { } } -impl<'a> RemoveSpaces<'a> for EGeneratesWith { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EGeneratesWith { + fn normalize(&self, _arena: &'a Bump) -> Self { match self { EGeneratesWith::Open(_) => EGeneratesWith::Open(Position::zero()), EGeneratesWith::With(_) => EGeneratesWith::With(Position::zero()), @@ -1632,8 +1589,8 @@ impl<'a> RemoveSpaces<'a> for EGeneratesWith { } } -impl<'a> RemoveSpaces<'a> for EGenerates { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EGenerates { + fn normalize(&self, _arena: &'a Bump) -> Self { match self { EGenerates::Open(_) => EGenerates::Open(Position::zero()), EGenerates::Generates(_) => EGenerates::Generates(Position::zero()), @@ -1646,8 +1603,8 @@ impl<'a> RemoveSpaces<'a> for EGenerates { } } -impl<'a> RemoveSpaces<'a> for EPackages<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EPackages<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EPackages::Open(_) => EPackages::Open(Position::zero()), EPackages::Space(inner_err, _) => EPackages::Space(*inner_err, Position::zero()), @@ -1658,50 +1615,50 @@ impl<'a> RemoveSpaces<'a> for EPackages<'a> { EPackages::IndentListStart(_) => EPackages::IndentListStart(Position::zero()), EPackages::IndentListEnd(_) => EPackages::IndentListEnd(Position::zero()), EPackages::PackageEntry(inner_err, _) => { - EPackages::PackageEntry(inner_err.remove_spaces(arena), Position::zero()) + EPackages::PackageEntry(inner_err.normalize(arena), Position::zero()) } } } } -impl<'a> RemoveSpaces<'a> for EHeader<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EHeader<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EHeader::Provides(inner_err, _) => { - EHeader::Provides(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Provides(inner_err.normalize(arena), Position::zero()) } EHeader::Params(inner_err, _) => { - EHeader::Params(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Params(inner_err.normalize(arena), Position::zero()) } EHeader::Exposes(inner_err, _) => { - EHeader::Exposes(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Exposes(inner_err.normalize(arena), Position::zero()) } EHeader::Imports(inner_err, _) => { - EHeader::Imports(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Imports(inner_err.normalize(arena), Position::zero()) } EHeader::Requires(inner_err, _) => { - EHeader::Requires(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Requires(inner_err.normalize(arena), Position::zero()) } EHeader::Packages(inner_err, _) => { - EHeader::Packages(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Packages(inner_err.normalize(arena), Position::zero()) } EHeader::Generates(inner_err, _) => { - EHeader::Generates(inner_err.remove_spaces(arena), Position::zero()) + EHeader::Generates(inner_err.normalize(arena), Position::zero()) } EHeader::GeneratesWith(inner_err, _) => { - EHeader::GeneratesWith(inner_err.remove_spaces(arena), Position::zero()) + EHeader::GeneratesWith(inner_err.normalize(arena), Position::zero()) } EHeader::Space(inner_err, _) => EHeader::Space(*inner_err, Position::zero()), EHeader::Start(_) => EHeader::Start(Position::zero()), EHeader::ModuleName(_) => EHeader::ModuleName(Position::zero()), EHeader::AppName(inner_err, _) => { - EHeader::AppName(inner_err.remove_spaces(arena), Position::zero()) + EHeader::AppName(inner_err.normalize(arena), Position::zero()) } EHeader::PackageName(inner_err, _) => { - EHeader::PackageName(inner_err.remove_spaces(arena), Position::zero()) + EHeader::PackageName(inner_err.normalize(arena), Position::zero()) } EHeader::PlatformName(inner_err, _) => { - EHeader::PlatformName(inner_err.remove_spaces(arena), Position::zero()) + EHeader::PlatformName(inner_err.normalize(arena), Position::zero()) } EHeader::IndentStart(_) => EHeader::IndentStart(Position::zero()), EHeader::InconsistentModuleName(_) => EHeader::InconsistentModuleName(Region::zero()), @@ -1709,11 +1666,11 @@ impl<'a> RemoveSpaces<'a> for EHeader<'a> { } } -impl<'a> RemoveSpaces<'a> for EPackageName<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EPackageName<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EPackageName::BadPath(inner_err, _) => { - EPackageName::BadPath(inner_err.remove_spaces(arena), Position::zero()) + EPackageName::BadPath(inner_err.normalize(arena), Position::zero()) } EPackageName::Escapes(_) => EPackageName::Escapes(Position::zero()), EPackageName::Multiline(_) => EPackageName::Multiline(Position::zero()), @@ -1721,8 +1678,8 @@ impl<'a> RemoveSpaces<'a> for EPackageName<'a> { } } -impl<'a> RemoveSpaces<'a> for SyntaxError<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for SyntaxError<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { SyntaxError::Unexpected(_) => SyntaxError::Unexpected(Region::zero()), SyntaxError::OutdentedTooFar => SyntaxError::OutdentedTooFar, @@ -1735,23 +1692,21 @@ impl<'a> RemoveSpaces<'a> for SyntaxError<'a> { } SyntaxError::NotYetImplemented(text) => SyntaxError::NotYetImplemented(text.clone()), SyntaxError::Todo => SyntaxError::Todo, - SyntaxError::Type(err) => SyntaxError::Type(err.remove_spaces(arena)), - SyntaxError::Pattern(err) => SyntaxError::Pattern(err.remove_spaces(arena)), - SyntaxError::Expr(err, _) => { - SyntaxError::Expr(err.remove_spaces(arena), Position::zero()) - } - SyntaxError::Header(err) => SyntaxError::Header(err.remove_spaces(arena)), + SyntaxError::Type(err) => SyntaxError::Type(err.normalize(arena)), + SyntaxError::Pattern(err) => SyntaxError::Pattern(err.normalize(arena)), + SyntaxError::Expr(err, _) => SyntaxError::Expr(err.normalize(arena), Position::zero()), + SyntaxError::Header(err) => SyntaxError::Header(err.normalize(arena)), SyntaxError::Space(inner_err) => SyntaxError::Space(*inner_err), SyntaxError::NotEndOfFile(_) => SyntaxError::NotEndOfFile(Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for EPackageEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EPackageEntry<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EPackageEntry::BadPackage(inner_err, _) => { - EPackageEntry::BadPackage(inner_err.remove_spaces(arena), Position::zero()) + EPackageEntry::BadPackage(inner_err.normalize(arena), Position::zero()) } EPackageEntry::Shorthand(_) => EPackageEntry::Shorthand(Position::zero()), EPackageEntry::Colon(_) => EPackageEntry::Colon(Position::zero()), @@ -1765,8 +1720,8 @@ impl<'a> RemoveSpaces<'a> for EPackageEntry<'a> { } } -impl<'a> RemoveSpaces<'a> for EProvides<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EProvides<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EProvides::Provides(_) => EProvides::Provides(Position::zero()), EProvides::Open(_) => EProvides::Open(Position::zero()), @@ -1779,18 +1734,18 @@ impl<'a> RemoveSpaces<'a> for EProvides<'a> { EProvides::ListEnd(_) => EProvides::ListEnd(Position::zero()), EProvides::Identifier(_) => EProvides::Identifier(Position::zero()), EProvides::Package(inner_err, _) => { - EProvides::Package(inner_err.remove_spaces(arena), Position::zero()) + EProvides::Package(inner_err.normalize(arena), Position::zero()) } EProvides::Space(inner_err, _) => EProvides::Space(*inner_err, Position::zero()), } } } -impl<'a> RemoveSpaces<'a> for EParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EParams<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { EParams::Pattern(inner_err, _) => { - EParams::Pattern(inner_err.remove_spaces(arena), Position::zero()) + EParams::Pattern(inner_err.normalize(arena), Position::zero()) } EParams::BeforeArrow(_) => EParams::BeforeArrow(Position::zero()), EParams::Arrow(_) => EParams::Arrow(Position::zero()), @@ -1800,8 +1755,8 @@ impl<'a> RemoveSpaces<'a> for EParams<'a> { } } -impl<'a> RemoveSpaces<'a> for EExposes { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EExposes { + fn normalize(&self, _arena: &'a Bump) -> Self { match self { EExposes::Exposes(_) => EExposes::Exposes(Position::zero()), EExposes::Open(_) => EExposes::Open(Position::zero()), @@ -1815,8 +1770,8 @@ impl<'a> RemoveSpaces<'a> for EExposes { } } -impl<'a> RemoveSpaces<'a> for EImports { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for EImports { + fn normalize(&self, _arena: &'a Bump) -> Self { match self { EImports::Open(_) => EImports::Open(Position::zero()), EImports::Imports(_) => EImports::Imports(Position::zero()), @@ -1841,8 +1796,8 @@ impl<'a> RemoveSpaces<'a> for EImports { } } -impl<'a> RemoveSpaces<'a> for ERequires<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ERequires<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ERequires::Requires(_) => ERequires::Requires(Position::zero()), ERequires::Open(_) => ERequires::Open(Position::zero()), @@ -1851,7 +1806,7 @@ impl<'a> RemoveSpaces<'a> for ERequires<'a> { ERequires::ListStart(_) => ERequires::ListStart(Position::zero()), ERequires::ListEnd(_) => ERequires::ListEnd(Position::zero()), ERequires::TypedIdent(inner_err, _) => { - ERequires::TypedIdent(inner_err.remove_spaces(arena), Position::zero()) + ERequires::TypedIdent(inner_err.normalize(arena), Position::zero()) } ERequires::Rigid(_) => ERequires::Rigid(Position::zero()), ERequires::Space(inner_err, _) => ERequires::Space(*inner_err, Position::zero()), @@ -1859,15 +1814,15 @@ impl<'a> RemoveSpaces<'a> for ERequires<'a> { } } -impl<'a> RemoveSpaces<'a> for ETypedIdent<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for ETypedIdent<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { ETypedIdent::Space(inner_err, _) => ETypedIdent::Space(*inner_err, Position::zero()), ETypedIdent::HasType(_) => ETypedIdent::HasType(Position::zero()), ETypedIdent::IndentHasType(_) => ETypedIdent::IndentHasType(Position::zero()), ETypedIdent::Name(_) => ETypedIdent::Name(Position::zero()), ETypedIdent::Type(inner_err, _) => { - ETypedIdent::Type(inner_err.remove_spaces(arena), Position::zero()) + ETypedIdent::Type(inner_err.normalize(arena), Position::zero()) } ETypedIdent::IndentType(_) => ETypedIdent::IndentType(Position::zero()), ETypedIdent::Identifier(_) => ETypedIdent::Identifier(Position::zero()), diff --git a/crates/compiler/test_syntax/src/minimize.rs b/crates/compiler/test_syntax/src/minimize.rs index 946b31147e0..7cd87bd9227 100644 --- a/crates/compiler/test_syntax/src/minimize.rs +++ b/crates/compiler/test_syntax/src/minimize.rs @@ -7,7 +7,7 @@ use crate::test_helpers::{Input, InputKind}; use bumpalo::Bump; -use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces}; +use roc_parse::{ast::Malformed, normalize::Normalize}; pub fn print_minimizations(text: &str, kind: InputKind) { let Some(original_error) = round_trip_once_and_extract_error(text, kind) else { @@ -81,12 +81,7 @@ fn round_trip_once(input: Input<'_>) -> Option { let actual = match input.parse_in(&arena) { Ok(a) => a, - Err(e) => { - return Some(format!( - "Initial parse failed: {:?}", - e.remove_spaces(&arena) - )) - } + Err(e) => return Some(format!("Initial parse failed: {:?}", e.normalize(&arena))), }; if actual.is_malformed() { @@ -97,11 +92,11 @@ fn round_trip_once(input: Input<'_>) -> Option { let reparsed_ast = match output.as_ref().parse_in(&arena) { Ok(r) => r, - Err(e) => return Some(format!("Reparse failed: {:?}", e.remove_spaces(&arena))), + Err(e) => return Some(format!("Reparse failed: {:?}", e.normalize(&arena))), }; - let ast_normalized = actual.remove_spaces(&arena); - let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena); + let ast_normalized = actual.normalize(&arena); + let reparsed_ast_normalized = reparsed_ast.normalize(&arena); if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") { return Some("Different ast".to_string()); diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index 140e1092499..c16c17d0011 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -3,8 +3,8 @@ use roc_fmt::{annotation::Formattable, header::fmt_header}; use roc_parse::{ ast::{Defs, Expr, FullAst, Header, Malformed, SpacesBefore}, header::parse_module_defs, + normalize::Normalize, parser::{Parser, SyntaxError}, - remove_spaces::RemoveSpaces, state::State, test_helpers::{parse_defs_with, parse_expr_with, parse_header_with}, }; @@ -128,13 +128,13 @@ impl<'a> Malformed for Output<'a> { } } -impl<'a> RemoveSpaces<'a> for Output<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { +impl<'a> Normalize<'a> for Output<'a> { + fn normalize(&self, arena: &'a Bump) -> Self { match self { - Output::Header(header) => Output::Header(header.remove_spaces(arena)), - Output::ModuleDefs(defs) => Output::ModuleDefs(defs.remove_spaces(arena)), - Output::Expr(expr) => Output::Expr(expr.remove_spaces(arena)), - Output::Full(full) => Output::Full(full.remove_spaces(arena)), + Output::Header(header) => Output::Header(header.normalize(arena)), + Output::ModuleDefs(defs) => Output::ModuleDefs(defs.normalize(arena)), + Output::Expr(expr) => Output::Expr(expr.normalize(arena)), + Output::Full(full) => Output::Full(full.normalize(arena)), } } } @@ -221,8 +221,8 @@ impl<'a> Input<'a> { ); }); - let ast_normalized = actual.remove_spaces(&arena); - let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena); + let ast_normalized = actual.normalize(&arena); + let reparsed_ast_normalized = reparsed_ast.normalize(&arena); // HACK! // We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast, diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index a01bae139e9..026605d825c 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -63,7 +63,7 @@ mod test_fmt { match header::parse_header(&arena, State::new(src.as_bytes())) { Ok((actual, state)) => { - use roc_parse::remove_spaces::RemoveSpaces; + use roc_parse::normalize::Normalize; let mut buf = Buf::new_in(&arena); @@ -77,8 +77,8 @@ mod test_fmt { ); }); - let ast_normalized = actual.remove_spaces(&arena); - let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena); + let ast_normalized = actual.normalize(&arena); + let reparsed_ast_normalized = reparsed_ast.normalize(&arena); // HACK! // We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast, From 723502c498b3e09e59ffabca1947e52d0ea6ee9d Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:32:16 +0200 Subject: [PATCH 164/203] try more recent valgrind Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- .github/workflows/ubuntu_x86_64_debug.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu_x86_64_debug.yml b/.github/workflows/ubuntu_x86_64_debug.yml index 0be11ffad91..4e58cc8dfe0 100644 --- a/.github/workflows/ubuntu_x86_64_debug.yml +++ b/.github/workflows/ubuntu_x86_64_debug.yml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies run: | - sudo apt -y install build-essential libunwind-dev pkg-config zlib1g-dev valgrind + sudo apt -y install build-essential libunwind-dev pkg-config zlib1g-dev wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 @@ -30,6 +30,7 @@ jobs: sudo ln -s /usr/bin/clang-16 /usr/bin/clang sudo ln -s /usr/bin/lld-16 /usr/bin/ld.lld sudo apt -y install libpolly-16-dev + sudo snap install valgrind --classic - name: Check if debug flag files are in sync run: ./ci/check_debug_vars.sh From 065a2237a7ba52ab356bee62340e29f099850869 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:02:59 +0200 Subject: [PATCH 165/203] install valgrind dependency Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- .github/workflows/ubuntu_x86_64_debug.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu_x86_64_debug.yml b/.github/workflows/ubuntu_x86_64_debug.yml index 4e58cc8dfe0..d324a64906a 100644 --- a/.github/workflows/ubuntu_x86_64_debug.yml +++ b/.github/workflows/ubuntu_x86_64_debug.yml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies run: | - sudo apt -y install build-essential libunwind-dev pkg-config zlib1g-dev + sudo apt -y install build-essential libunwind-dev pkg-config zlib1g-dev libc6-dbg wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 From 3b07afc72e9b5c36c82b20575c246e88f5923f41 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:28:59 +0200 Subject: [PATCH 166/203] nix for debug tests --- .github/workflows/ci_manager.yml | 6 ++-- .github/workflows/ubuntu_x86_64_debug.yml | 42 ----------------------- 2 files changed, 3 insertions(+), 45 deletions(-) delete mode 100644 .github/workflows/ubuntu_x86_64_debug.yml diff --git a/.github/workflows/ci_manager.yml b/.github/workflows/ci_manager.yml index 8d18f8dc996..f0548533f33 100644 --- a/.github/workflows/ci_manager.yml +++ b/.github/workflows/ci_manager.yml @@ -104,10 +104,10 @@ jobs: if: needs.check-changes.outputs.run_tests == 'full' uses: ./.github/workflows/ubuntu_x86_64.yml - start-ubuntu-x86-64-tests-debug: + start-ubuntu-x86-64-nix-tests-debug: needs: check-changes if: needs.check-changes.outputs.run_tests == 'full' - uses: ./.github/workflows/ubuntu_x86_64_debug.yml + uses: ./.github/workflows/ubuntu_x86_64_nix_debug.yml start-windows-release-build-test: needs: check-changes @@ -133,7 +133,7 @@ jobs: start-nix-macos-apple-silicon-tests, start-macos-x86-64-tests, start-ubuntu-x86-64-tests, - start-ubuntu-x86-64-tests-debug, + start-ubuntu-x86-64-nix-tests-debug, start-windows-release-build-test, start-windows-tests, start-roc-benchmarks diff --git a/.github/workflows/ubuntu_x86_64_debug.yml b/.github/workflows/ubuntu_x86_64_debug.yml deleted file mode 100644 index 0be11ffad91..00000000000 --- a/.github/workflows/ubuntu_x86_64_debug.yml +++ /dev/null @@ -1,42 +0,0 @@ -on: - workflow_call: - -name: cargo test debug - -env: - RUST_BACKTRACE: 1 - -jobs: - cargo-test-debug: - name: cargo test debug - runs-on: [ubuntu-22.04] - timeout-minutes: 90 - steps: - - uses: actions/checkout@v4 - - - uses: goto-bus-stop/setup-zig@v2 - with: - version: 0.11.0 - - - run: zig version - - - name: Install dependencies - run: | - sudo apt -y install build-essential libunwind-dev pkg-config zlib1g-dev valgrind - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 16 - sudo rm /usr/bin/clang - sudo ln -s /usr/bin/clang-16 /usr/bin/clang - sudo ln -s /usr/bin/lld-16 /usr/bin/ld.lld - sudo apt -y install libpolly-16-dev - - - name: Check if debug flag files are in sync - run: ./ci/check_debug_vars.sh - - # for skipped tests; see #6946, #6947 - - name: cargo test without --release - env: - RUSTFLAGS: -C link-arg=-fuse-ld=lld - ROC_CHECK_MONO_IR: 1 - run: cargo test -- --skip tests/exhaustive/match_on_result_with_uninhabited_error_destructuring_in_lambda_syntax.txt --skip tests::identity_lambda --skip tests::issue_2300 --skip tests::issue_2582_specialize_result_value --skip tests::sum_lambda From a5ae2608ea3295bf7a274fbeb1a8ff325b645833 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Wed, 14 Aug 2024 09:13:57 +1000 Subject: [PATCH 167/203] fix incorrect name from merge --- crates/compiler/parse/src/normalize.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 84ac34304ae..3d69dc36571 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -189,7 +189,7 @@ impl<'a> Normalize<'a> for Header<'a> { impl<'a> Normalize<'a> for ModuleParams<'a> { fn normalize(&self, arena: &'a Bump) -> Self { ModuleParams { - params: self.params.normalize(arena), + pattern: self.pattern.normalize(arena), before_arrow: &[], after_arrow: &[], } From 7aebee8c94053230536a6372617f60604ccf11a7 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Wed, 14 Aug 2024 13:16:24 +1000 Subject: [PATCH 168/203] implement Result.mapBoth and Result.map2 builtins --- crates/compiler/builtins/roc/Result.roc | 28 ++++++++++++- crates/compiler/module/src/symbol.rs | 2 + crates/compiler/test_gen/src/gen_result.rs | 46 ++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/crates/compiler/builtins/roc/Result.roc b/crates/compiler/builtins/roc/Result.roc index 58aecb1d76d..dd3e42c584c 100644 --- a/crates/compiler/builtins/roc/Result.roc +++ b/crates/compiler/builtins/roc/Result.roc @@ -1,4 +1,15 @@ -module [Result, isOk, isErr, map, mapErr, try, onErr, withDefault] +module [ + Result, + isOk, + isErr, + map, + mapErr, + mapBoth, + map2, + try, + onErr, + withDefault, +] import Bool exposing [Bool] @@ -67,6 +78,21 @@ mapErr = \result, transform -> Ok v -> Ok v Err e -> Err (transform e) +## Maps both the `Ok` and `Err` values of a `Result` to new values. +mapBoth : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2 +mapBoth = \result, okTransform, errTransform -> + result + |> Result.map okTransform + |> Result.mapErr errTransform + +## Maps the `Ok` values of two `Result`s to a new value using a given transformation. +map2 : Result a err, Result b err, (a, b -> c) -> Result c err +map2 = \firstResult, secondResult, transform -> + when (firstResult, secondResult) is + (Ok first, Ok second) -> Ok (transform first second) + (Err err, _) -> Err err + (_, Err err) -> Err err + ## If the result is `Ok`, transforms the entire result by running a conversion ## function on the value the `Ok` holds. Then returns that new result. If the ## result is `Err`, this has no effect. Use `onErr` to transform an `Err`. diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 2713be16aeb..36caa4f8e07 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1556,6 +1556,8 @@ define_builtins! { 5 RESULT_WITH_DEFAULT: "withDefault" 6 RESULT_TRY: "try" 7 RESULT_IS_OK: "isOk" + 8 RESULT_MAP_BOTH: "mapBoth" + 9 RESULT_MAP_TWO: "map2" } 8 DICT: "Dict" => { 0 DICT_DICT: "Dict" exposed_type=true // the Dict.Dict type alias diff --git a/crates/compiler/test_gen/src/gen_result.rs b/crates/compiler/test_gen/src/gen_result.rs index 703d9fcd184..97acc389188 100644 --- a/crates/compiler/test_gen/src/gen_result.rs +++ b/crates/compiler/test_gen/src/gen_result.rs @@ -354,3 +354,49 @@ fn roc_result_after_err() { RocResult ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn roc_result_map_other() { + assert_evals_to!( + indoc!( + r#" + result : Result I64 I64 + result = Ok 42 + + result |> Result.mapBoth Num.toStr Num.toStr + "# + ), + RocResult::ok(RocStr::from("42")), + RocResult + ); + + assert_evals_to!( + indoc!( + r#" + result : Result I64 I64 + result = Err 24 + + result |> Result.mapBoth Num.toStr Num.toStr + "# + ), + RocResult::err(RocStr::from("24")), + RocResult + ); + + assert_evals_to!( + indoc!( + r#" + first : Result I64 Str + first = Ok 24 + + second : Result I64 Str + second = Ok -10 + + Result.map2 first second \a, b -> a + b + "# + ), + RocResult::ok(14i64), + RocResult + ); +} From 45f18bd64eacd9e97b8075fcf396068eb8785e5b Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Wed, 14 Aug 2024 13:17:52 +1000 Subject: [PATCH 169/203] update mono snapshots --- ...ose_correct_recursion_var_under_record.txt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt index 97679186871..cdc2a71912c 100644 --- a/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt +++ b/crates/compiler/test_mono/generated/choose_correct_recursion_var_under_record.txt @@ -97,17 +97,17 @@ procedure Num.51 (#Attr.2, #Attr.3): let Num.281 : U64 = lowlevel NumAddWrap #Attr.2 #Attr.3; ret Num.281; -procedure Result.5 (Result.10, Result.11): - let Result.37 : U8 = 1i64; - let Result.38 : U8 = GetTagId Result.10; - let Result.39 : Int1 = lowlevel Eq Result.37 Result.38; - if Result.39 then - dec Result.11; - let Result.12 : Str = UnionAtIndex (Id 1) (Index 0) Result.10; - ret Result.12; +procedure Result.5 (Result.12, Result.13): + let Result.52 : U8 = 1i64; + let Result.53 : U8 = GetTagId Result.12; + let Result.54 : Int1 = lowlevel Eq Result.52 Result.53; + if Result.54 then + dec Result.13; + let Result.14 : Str = UnionAtIndex (Id 1) (Index 0) Result.12; + ret Result.14; else - dec Result.10; - ret Result.11; + dec Result.12; + ret Result.13; procedure Test.10 (Test.11): let Test.12 : Str = CallByName Test.2 Test.11; From 6bae15c4673dbf030728790ca6b9d794dadfcb5c Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Wed, 14 Aug 2024 17:24:12 +1000 Subject: [PATCH 170/203] resolve review comments --- crates/compiler/builtins/roc/Result.roc | 9 ++--- crates/compiler/test_gen/src/gen_result.rs | 38 +++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/crates/compiler/builtins/roc/Result.roc b/crates/compiler/builtins/roc/Result.roc index dd3e42c584c..9e7a2b91042 100644 --- a/crates/compiler/builtins/roc/Result.roc +++ b/crates/compiler/builtins/roc/Result.roc @@ -81,11 +81,12 @@ mapErr = \result, transform -> ## Maps both the `Ok` and `Err` values of a `Result` to new values. mapBoth : Result ok1 err1, (ok1 -> ok2), (err1 -> err2) -> Result ok2 err2 mapBoth = \result, okTransform, errTransform -> - result - |> Result.map okTransform - |> Result.mapErr errTransform + when result is + Ok val -> Ok (okTransform val) + Err err -> Err (errTransform err) -## Maps the `Ok` values of two `Result`s to a new value using a given transformation. +## Maps the `Ok` values of two `Result`s to a new value using a given transformation, +## or returns the first `Err` value encountered. map2 : Result a err, Result b err, (a, b -> c) -> Result c err map2 = \firstResult, secondResult, transform -> when (firstResult, secondResult) is diff --git a/crates/compiler/test_gen/src/gen_result.rs b/crates/compiler/test_gen/src/gen_result.rs index 97acc389188..f9160a73a09 100644 --- a/crates/compiler/test_gen/src/gen_result.rs +++ b/crates/compiler/test_gen/src/gen_result.rs @@ -357,7 +357,7 @@ fn roc_result_after_err() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -fn roc_result_map_other() { +fn roc_result_map_both() { assert_evals_to!( indoc!( r#" @@ -383,7 +383,11 @@ fn roc_result_map_other() { RocResult::err(RocStr::from("24")), RocResult ); +} +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +fn roc_result_map_two() { assert_evals_to!( indoc!( r#" @@ -399,4 +403,36 @@ fn roc_result_map_other() { RocResult::ok(14i64), RocResult ); + + assert_evals_to!( + indoc!( + r#" + first : Result I64 Str + first = Err "foo" + + second : Result I64 Str + second = Err "bar" + + Result.map2 first second \a, b -> a + b + "# + ), + RocResult::err(RocStr::from("foo")), + RocResult + ); + + assert_evals_to!( + indoc!( + r#" + first : Result I64 Str + first = Ok 42 + + second : Result I64 Str + second = Err "bar" + + Result.map2 first second \a, b -> a + b + "# + ), + RocResult::err(RocStr::from("bar")), + RocResult + ); } From bd4ef6aba71851a50067b1658403a3a39f526a00 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Wed, 14 Aug 2024 12:10:37 +0200 Subject: [PATCH 171/203] adjust getting started Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- www/content/install/getting_started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/content/install/getting_started.md b/www/content/install/getting_started.md index 623e36d37cd..714e7b1c507 100644 --- a/www/content/install/getting_started.md +++ b/www/content/install/getting_started.md @@ -11,7 +11,7 @@ If you have a specific question, the [FAQ](/faq) might have an answer, although ## [Installation](#installation){#installation} - [🐧 Linux x86_64](/install/linux_x86_64) -- [❄️ Nix Linux/MacOS](/install/nix) +- [❄️ Nix](/install/nix) - [🍏 MacOS Apple Silicon](/install/macos_apple_silicon) - [🍏 MacOS x86_64](/install/macos_x86_64) - [🟦 Windows](/install/windows) From 47978c8a2062f1e4509bcfb7e0f9437eb467c6b9 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:01:17 +0200 Subject: [PATCH 172/203] use macos codesign Anaconda also has a codesign and we want to use the macos one, see https://github.com/pyinstaller/pyinstaller/issues/8581#issuecomment-2147311833 Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- crates/compiler/build/src/link.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/build/src/link.rs b/crates/compiler/build/src/link.rs index 5195d4cd646..60119dc57a4 100644 --- a/crates/compiler/build/src/link.rs +++ b/crates/compiler/build/src/link.rs @@ -1159,7 +1159,7 @@ fn link_macos( Architecture::Aarch64 => { ld_child.wait()?; - let mut codesign_cmd = Command::new("codesign"); + let mut codesign_cmd = Command::new("/usr/bin/codesign"); codesign_cmd.args(["-s", "-", output_path.to_str().unwrap()]); debug_print_command(&codesign_cmd); let codesign_child = codesign_cmd.spawn()?; From 6ce5e75835be5dde9346b1b00e2665e0b14abd32 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Tue, 13 Aug 2024 22:48:50 -0700 Subject: [PATCH 173/203] Update docs for new record builders --- www/content/tutorial.md | 63 ++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index 78ff40a1af0..152e83e82ab 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -2226,33 +2226,62 @@ For this reason, any time you see a function that only runs a `when` on its only ### [Record Builder](#record-builder) {#record-builder} -The record builder syntax sugar is a useful feature which leverages the functional programming concept of [applicative functors](https://lucamug.medium.com/functors-applicatives-and-monads-in-pictures-784c2b5786f7), to provide a flexible method for constructing complex types. +Record builders are a syntax sugar for sequencing actions and collecting the intermediate results as fields in a record. All you need to build a record is a `map2`-style function that takes two values of the same type and combines them using a provided combiner function. There are many convenient APIs we can build with this simple syntax. -The record builder syntax sugar helps to build up a record by applying a series of functions to it. +For example, let's say we want a record builder to match URLs as follows: -For example, let's say we write a record-builder as follows: +```roc +combineMatchers : UrlMatcher a, UrlMatcher b, (a, b -> c) -> UrlMatcher c + +userTabMatcher = + { combineMatchers <- + users: exactSegment "users", + userId: u64Segment, + tab: anySegment, + } + +expect + userTabMatcher + |> matchOnUrl "/users/123/account" + == Ok { users: {}, userId: 123, tab: "account" } +``` + +The `userTabMatcher` record builder desugars to the following: ```roc -{ aliceID, bobID, trudyID } = - initIDCount { - aliceID: <- incID, - bobID: <- incID, - trudyID: <- incID, - } |> extractState +userTabMatcher = + combineMatchers + (exactSegment "users") + ( + combineMatchers + u64Segment + anySegment + \userId, tab -> (userId, tab) + ) + \users, (userId, tab) -> { users, userId, tab } ``` -The above desguars to the following. +You can see that the `combineMatchers` builder function is simply applied in sequence, pairing up all fields until a record is created. + +You'll notice that the `users` field above holds an empty record, and isn't a useful part of the result. If you want to ignore such a field in the record builder, prefix its name with an underscore as you would do to ignore a variable: ```roc -{ aliceID, bobID, trudyID } = - initIDCount (\aID -> \bID -> \cID -> { aliceID: aID, bobID: bID, trudyID: cID }) - |> incID - |> incID - |> incID - |> extractState +userTabMatcher = + { combineMatchers <- + _: exactSegment "users", + userId: u64Segment, + _tab: anySegment, + } + +expect + userTabMatcher + |> matchOnUrl "/users/123/account" + == Ok { userId: 123 } ``` -See the [Record Builder Example](https://www.roc-lang.org/examples/RecordBuilder/README.html) for an explanation of how to use this feature. +If you want to see other examples of using record builders, look at the [Record Builder Example](https://www.roc-lang.org/examples/RecordBuilder/README.html) for a moderately-sized example or the [Arg.Builder](https://github.com/roc-lang/basic-cli/blob/main/platform/Arg/Builder.roc) module in our `basic-cli` platform for a complex example. + +_Note: This syntax replaces the old `field: <- value` record builder syntax using applicative functors because it is much simpler to understand and use. The old syntax will be removed soon._ ### [Reserved Keywords](#reserved-keywords) {#reserved-keywords} From c8a2408c7115db17d34d3a2c43bd276080ec7cb5 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 10:23:27 -0700 Subject: [PATCH 174/203] Add type annotations for builder examples --- www/content/tutorial.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index 152e83e82ab..3adc8fafe14 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -2232,7 +2232,9 @@ For example, let's say we want a record builder to match URLs as follows: ```roc combineMatchers : UrlMatcher a, UrlMatcher b, (a, b -> c) -> UrlMatcher c +combineMatchers = \matcherA, matcherB, combiner -> ... +userTabMatcher : UrlMatcher { users: {}, userId: U64, tab: Str } userTabMatcher = { combineMatchers <- users: exactSegment "users", @@ -2266,6 +2268,7 @@ You can see that the `combineMatchers` builder function is simply applied in seq You'll notice that the `users` field above holds an empty record, and isn't a useful part of the result. If you want to ignore such a field in the record builder, prefix its name with an underscore as you would do to ignore a variable: ```roc +userTabMatcher : UrlMatcher { userId: U64 } userTabMatcher = { combineMatchers <- _: exactSegment "users", From 3137cb30fa987174ff20fa04687b330388cf5b33 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Thu, 15 Aug 2024 07:10:17 +1000 Subject: [PATCH 175/203] include shadowing in Plans --- www/content/plans.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/www/content/plans.md b/www/content/plans.md index 54d62474b3b..1aea18ba434 100644 --- a/www/content/plans.md +++ b/www/content/plans.md @@ -22,6 +22,12 @@ Implementing the very important [module params](https://docs.google.com/document Work has not started on this yet, but we'd like to have the project completed sometime in 2024. +### [Shadowing](#shadowing) {#shadowing} + +Shadowing is [currently disallowed](https://www.roc-lang.org/functional#no-reassignment), which means that once a name has been assigned to a value, nothing in the same scope can assign it again. + +The plan is to enable shadowing in a future re-write of the Canonicalisation pass as a trial to see if it's a good idea. If it turns out that shadowing isn't the best fit for roc, at least we will be able to provide a good warning for users. + ### Platform Author Specific Breaking Changes All of the following changes only affect platform authors. From 35e398b03688a4598454c415176c3f7b5b5443d9 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Thu, 15 Aug 2024 07:15:50 +1000 Subject: [PATCH 176/203] review comments --- www/content/plans.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/content/plans.md b/www/content/plans.md index 1aea18ba434..6fdd1327ae2 100644 --- a/www/content/plans.md +++ b/www/content/plans.md @@ -26,7 +26,7 @@ Work has not started on this yet, but we'd like to have the project completed so Shadowing is [currently disallowed](https://www.roc-lang.org/functional#no-reassignment), which means that once a name has been assigned to a value, nothing in the same scope can assign it again. -The plan is to enable shadowing in a future re-write of the Canonicalisation pass as a trial to see if it's a good idea. If it turns out that shadowing isn't the best fit for roc, at least we will be able to provide a good warning for users. +The plan is to enable shadowing in a future re-write of the [Canonicalization](https://en.wikipedia.org/wiki/Canonicalization) pass as a trial to see if it's a good idea. If it turns out that shadowing isn't the best fit for Roc, we'll remove it as we've done for other experiments, e.g. backpassing. ### Platform Author Specific Breaking Changes From 3bc5c1dc1218ad90af9ce377377c459a1ba96620 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 22:14:08 -0700 Subject: [PATCH 177/203] Combine ! and ? into single TrySuffix AST node --- crates/compiler/can/src/desugar.rs | 32 ++- crates/compiler/can/src/expr.rs | 4 +- crates/compiler/can/src/suffixed.rs | 200 +++++++++------- crates/compiler/fmt/src/expr.rs | 12 +- crates/compiler/module/src/called_via.rs | 4 + crates/compiler/parse/src/ast.rs | 26 ++- crates/compiler/parse/src/expr.rs | 16 +- crates/compiler/parse/src/ident.rs | 3 +- crates/compiler/parse/src/normalize.rs | 2 +- ...middle_extra_indents.moduledefs.result-ast | 14 +- .../pass/pizza_bang.moduledefs.result-ast | 21 +- .../snapshots/pass/suffixed.expr.result-ast | 10 - .../pass/suffixed_bang.expr.result-ast | 13 ++ ...ffixed.expr.roc => suffixed_bang.expr.roc} | 0 ...ng_multiple_defs.moduledefs.formatted.roc} | 0 ..._bang_multiple_defs.moduledefs.result-ast} | 21 +- ...uffixed_bang_multiple_defs.moduledefs.roc} | 0 ...> suffixed_bang_nested.expr.formatted.roc} | 0 ...t => suffixed_bang_nested.expr.result-ast} | 14 +- ...expr.roc => suffixed_bang_nested.expr.roc} | 0 ... suffixed_bang_one_def.full.formatted.roc} | 0 ... => suffixed_bang_one_def.full.result-ast} | 14 +- ...ull.roc => suffixed_bang_one_def.full.roc} | 0 ...xed_bang_optional_last.full.formatted.roc} | 0 ...ffixed_bang_optional_last.full.result-ast} | 7 +- ...c => suffixed_bang_optional_last.full.roc} | 0 .../pass/suffixed_question.expr.result-ast | 13 ++ .../snapshots/pass/suffixed_question.expr.roc | 1 + ...ion_multiple_defs.moduledefs.formatted.roc | 5 + ...estion_multiple_defs.moduledefs.result-ast | 119 ++++++++++ ...ixed_question_multiple_defs.moduledefs.roc | 5 + ...uffixed_question_nested.expr.formatted.roc | 1 + .../suffixed_question_nested.expr.result-ast | 45 ++++ .../pass/suffixed_question_nested.expr.roc | 1 + ...ffixed_question_one_def.full.formatted.roc | 17 ++ .../suffixed_question_one_def.full.result-ast | 213 ++++++++++++++++++ .../pass/suffixed_question_one_def.full.roc | 16 ++ ..._question_optional_last.full.formatted.roc | 9 + ...xed_question_optional_last.full.result-ast | 134 +++++++++++ .../suffixed_question_optional_last.full.roc | 12 + .../test_syntax/tests/test_snapshots.rs | 15 +- crates/language_server/src/analysis/tokens.rs | 2 +- 42 files changed, 859 insertions(+), 162 deletions(-) delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed.expr.roc => suffixed_bang.expr.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_multiple_defs.moduledefs.formatted.roc => suffixed_bang_multiple_defs.moduledefs.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_multiple_defs.moduledefs.result-ast => suffixed_bang_multiple_defs.moduledefs.result-ast} (86%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_multiple_defs.moduledefs.roc => suffixed_bang_multiple_defs.moduledefs.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_nested.expr.formatted.roc => suffixed_bang_nested.expr.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_nested.expr.result-ast => suffixed_bang_nested.expr.result-ast} (81%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_nested.expr.roc => suffixed_bang_nested.expr.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_one_def.full.formatted.roc => suffixed_bang_one_def.full.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_one_def.full.result-ast => suffixed_bang_one_def.full.result-ast} (94%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_one_def.full.roc => suffixed_bang_one_def.full.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_optional_last.full.formatted.roc => suffixed_bang_optional_last.full.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_optional_last.full.result-ast => suffixed_bang_optional_last.full.result-ast} (96%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_optional_last.full.roc => suffixed_bang_optional_last.full.roc} (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index bb4affaf720..75cc844a773 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -1,6 +1,6 @@ #![allow(clippy::manual_map)] -use crate::suffixed::{apply_task_await, unwrap_suffixed_expression, EUnwrapped}; +use crate::suffixed::{apply_try_function, unwrap_suffixed_expression, EUnwrapped}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_error_macros::internal_error; @@ -220,11 +220,20 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) sub_arg, sub_pat, sub_new, + target, }) => desugar_value_def_suffixed( arena, Body( loc_pattern, - apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None), + apply_try_function( + arena, + loc_expr.region, + sub_arg, + sub_pat, + sub_new, + None, + target, + ), ), ), Err(..) => Body( @@ -254,6 +263,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) sub_arg, sub_pat, sub_new, + target, }) => desugar_value_def_suffixed( arena, AnnotatedBody { @@ -261,13 +271,14 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) ann_type, lines_between, body_pattern, - body_expr: apply_task_await( + body_expr: apply_try_function( arena, body_expr.region, sub_arg, sub_pat, sub_new, Some((ann_pattern, ann_type)), + target, ), }, ), @@ -371,14 +382,23 @@ pub fn desugar_expr<'a>( arena.alloc(Loc { region, value }) } - // desugar the sub_expression, but leave the TaskAwaitBang as this will + // desugar the sub_expression, but leave the TrySuffix as this will // be unwrapped later in desugar_value_def_suffixed - TaskAwaitBang(sub_expr) => { + TrySuffix { + expr: sub_expr, + target, + } => { let intermediate = arena.alloc(Loc::at(loc_expr.region, **sub_expr)); let new_sub_loc_expr = desugar_expr(arena, intermediate, src, line_info, module_path); let new_sub_expr = arena.alloc(new_sub_loc_expr.value); - arena.alloc(Loc::at(loc_expr.region, TaskAwaitBang(new_sub_expr))) + arena.alloc(Loc::at( + loc_expr.region, + TrySuffix { + expr: new_sub_expr, + target: *target, + }, + )) } RecordAccess(sub_expr, paths) => { let region = loc_expr.region; diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 86c372d7004..9dd672fdf54 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1119,7 +1119,7 @@ pub fn canonicalize_expr<'a>( output, ) } - ast::Expr::TaskAwaitBang(..) => internal_error!("a Expr::TaskAwaitBang expression was not completely removed in desugar_value_def_suffixed"), + ast::Expr::TrySuffix { .. } => internal_error!("a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"), ast::Expr::Tag(tag) => { let variant_var = var_store.fresh(); let ext_var = var_store.fresh(); @@ -2515,7 +2515,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { ast::Expr::TupleAccess(sub_expr, _) | ast::Expr::ParensAround(sub_expr) | ast::Expr::RecordAccess(sub_expr, _) - | ast::Expr::TaskAwaitBang(sub_expr) => is_valid_interpolation(sub_expr), + | ast::Expr::TrySuffix { expr: sub_expr, .. } => is_valid_interpolation(sub_expr), ast::Expr::Apply(loc_expr, args, _called_via) => { is_valid_interpolation(&loc_expr.value) && args diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index e70c3dfc669..251ec683ae9 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -6,7 +6,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::ModuleName; use roc_parse::ast::Expr::{self, *}; -use roc_parse::ast::{is_expr_suffixed, Pattern, TypeAnnotation, ValueDef, WhenBranch}; +use roc_parse::ast::{is_expr_suffixed, Pattern, TryTarget, TypeAnnotation, ValueDef, WhenBranch}; use roc_region::all::{Loc, Region}; use std::cell::Cell; @@ -34,14 +34,17 @@ pub enum EUnwrapped<'a> { /// e.g. x = first! (second! 42) /// The first unwrap will produce /// `UnwrappedDefExpr` - UnwrappedDefExpr(&'a Loc>), + UnwrappedDefExpr { + loc_expr: &'a Loc>, + target: TryTarget, + }, /// Suffixed sub expression /// e.g. x = first! (second! 42) /// In this example, the second unwrap (after unwrapping the top level `first!`) will produce /// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>` UnwrappedSubExpr { - /// the unwrapped expression argument for Task.await + /// the unwrapped expression argument for `try` functions sub_arg: &'a Loc>, /// the pattern for the closure @@ -49,6 +52,9 @@ pub enum EUnwrapped<'a> { /// the expression to replace the unwrapped sub_new: &'a Loc>, + + /// The type of the target for the suffix, e.g. a Task or Result + target: TryTarget, }, /// Malformed use of the suffix @@ -59,12 +65,13 @@ fn init_unwrapped_err<'a>( arena: &'a Bump, unwrapped_expr: &'a Loc>, maybe_def_pat: Option<&'a Loc>>, + target: TryTarget, ) -> Result<&'a Loc>, EUnwrapped<'a>> { match maybe_def_pat { Some(..) => { // we have a def pattern, so no need to generate a new pattern // as this should only be created in the first call from a def - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) } None => { // Provide an intermediate answer expression and pattern when unwrapping a @@ -87,13 +94,14 @@ fn init_unwrapped_err<'a>( sub_arg: unwrapped_expr, sub_pat, sub_new, + target, }) } } } /// Descend through the AST and unwrap each suffixed expression -/// when an expression is unwrapped, we apply a `Task.await` and +/// when an expression is unwrapped, we apply the appropriate try function and /// then descend through the AST again until there are no more suffixed /// expressions, or we hit an error pub fn unwrap_suffixed_expression<'a>( @@ -103,10 +111,13 @@ pub fn unwrap_suffixed_expression<'a>( ) -> Result<&'a Loc>, EUnwrapped<'a>> { let unwrapped_expression = { match loc_expr.value { - Expr::TaskAwaitBang(sub_expr) => { + Expr::TrySuffix { + expr: sub_expr, + target, + } => { let unwrapped_sub_expr = arena.alloc(Loc::at(loc_expr.region, *sub_expr)); - init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat) + init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat, target) } Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat), @@ -154,15 +165,16 @@ pub fn unwrap_suffixed_expression<'a>( .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); return Ok(new_expect); } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target, }) => { let new_expect = arena .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); - Err(EUnwrapped::UnwrappedDefExpr(new_expect)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_expect, target }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, sub_pat, sub_new, + target, }) => { let new_expect = arena .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); @@ -170,6 +182,7 @@ pub fn unwrap_suffixed_expression<'a>( sub_arg: new_expect, sub_pat, sub_new, + target, }) } Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), @@ -208,13 +221,14 @@ pub fn unwrap_suffixed_expression_parens_help<'a>( )); Ok(new_parens) } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { let new_parens = arena.alloc(Loc::at( loc_expr.region, @@ -224,6 +238,7 @@ pub fn unwrap_suffixed_expression_parens_help<'a>( sub_arg, sub_pat, sub_new: new_parens, + target, }) } Err(err) => Err(err), @@ -247,13 +262,13 @@ pub fn unwrap_suffixed_expression_closure_help<'a>( let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr))); Ok(new_closure) } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_closure_loc_ret = apply_try_function(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None, target); let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret))); Ok(new_closure) } Err(err) => { - debug_assert!(false,"the closure Defs was malformd, got {:#?}", err); + debug_assert!(false,"the closure Defs was malformed, got {:#?}", err); Err(EUnwrapped::Malformed) } } @@ -278,22 +293,22 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( Ok(new_arg) => { *arg = new_arg; } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unreachable, unwrapped arg cannot be def expression as `None` was passed as pattern"); } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg, target }) => { *arg = new_arg; let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via))); - return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply}); + return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply, target }); } Err(err) => return Err(err), } } // special case for when our Apply function is a suffixed Var (but not multiple suffixed) - if let Expr::TaskAwaitBang(sub_expr) = function.value { + if let Expr::TrySuffix { expr: sub_expr, target } = function.value { let unwrapped_function = arena.alloc(Loc::at( loc_expr.region, *sub_expr, @@ -301,7 +316,7 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); - return init_unwrapped_err(arena, new_apply, maybe_def_pat); + return init_unwrapped_err(arena, new_apply, maybe_def_pat, target); } // function is another expression @@ -310,15 +325,15 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via))); Ok(new_apply) } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_function)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_function, target }) => { let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); - Err(EUnwrapped::UnwrappedDefExpr(new_apply)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target }) } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => { let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via))); - - Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply}) + + Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target }) } Err(err) => Err(err) } @@ -361,21 +376,23 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { - let unwrapped_expression = apply_task_await( + let unwrapped_expression = apply_try_function( arena, sub_arg.region, sub_arg, sub_pat, sub_new, None, + target, ); let mut new_if_thens = Vec::new_in(arena); @@ -422,13 +439,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { if before.is_empty() { let mut new_if_thens = Vec::new_in(arena); @@ -445,13 +463,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( ), )); - let unwrapped_if_then = apply_task_await( + let unwrapped_if_then = apply_try_function( arena, sub_arg.region, sub_arg, sub_pat, new_if, None, + target, ); return unwrap_suffixed_expression( @@ -473,13 +492,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( ), )); - let after_if_then = apply_task_await( + let after_if_then = apply_try_function( arena, sub_arg.region, sub_arg, sub_pat, after_if, None, + target, ); let before_if_then = arena.alloc(Loc::at( @@ -507,16 +527,17 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( Expr::If(if_thens, unwrapped_final_else), ))); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { let unwrapped_final_else = - apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None); + apply_try_function(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None, target); let new_if = arena.alloc(Loc::at( loc_expr.region, @@ -551,7 +572,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>( if is_expr_suffixed(&branch_loc_expr.value) { let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) { Ok(unwrapped_branch_value) => unwrapped_branch_value, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None), + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => apply_try_function(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None, target), Err(..) => return Err(EUnwrapped::Malformed), }; @@ -578,12 +599,12 @@ pub fn unwrap_suffixed_expression_when_help<'a>( let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches))); Ok(new_when) } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches))); - let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when, None); + let applied_task_await = apply_try_function(arena,loc_expr.region,sub_arg,sub_pat,new_when, None, target); Ok(applied_task_await) } - Err(EUnwrapped::UnwrappedDefExpr(..)) + Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed) } @@ -631,7 +652,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( current_value_def.replace_expr(unwrapped_def); local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region); } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { let split_defs = local_defs.split_defs_around(tag_index); let before_empty = split_defs.before.is_empty(); let after_empty = split_defs.after.is_empty(); @@ -640,48 +661,60 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( // We pass None as a def pattern here because it's desugaring of the ret expression let next_expr = match unwrap_suffixed_expression(arena,loc_ret, None) { Ok(next_expr) => next_expr, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { // We need to apply Task.ok here as the defs final expression was unwrapped - apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None) + apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target) } - Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up return Err(EUnwrapped::Malformed); }, }; - return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat); + return unwrap_suffixed_expression( + arena, + apply_try_function( + arena, + def_expr.region, + unwrapped_expr, + def_pattern, + next_expr, + ann_type, + target, + ), + maybe_def_pat + ); } else if before_empty { // NIL before, SOME after -> FIRST DEF let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret))); let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){ Ok(next_expr) => next_expr, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None) + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target) } - Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up return Err(EUnwrapped::Malformed); }, }; - return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr,ann_type), maybe_def_pat); + return unwrap_suffixed_expression(arena, apply_try_function(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type, target), maybe_def_pat); } else if after_empty { // SOME before, NIL after -> LAST DEF // We pass None as a def pattern here because it's desugaring of the ret expression match unwrap_suffixed_expression(arena,loc_ret,None){ Ok(new_loc_ret) => { - let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); + let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); }, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None); - let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_loc_ret = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target); + let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { // TODO confirm this is correct with test case return Err(EUnwrapped::Malformed); } @@ -695,28 +728,28 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){ Ok(new_loc_ret) => { - let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); + let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); }, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None); - let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_loc_ret = apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target); + let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); } - Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up return Err(EUnwrapped::Malformed); }, }; } } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { let new_body_def = ValueDef::Body(def_pattern, sub_new); local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region); let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret))); - let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type); + let replaced_def = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type, target); return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat); } Err(err) => return Err(err) @@ -730,12 +763,12 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( Ok(new_loc_ret) => { Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)))) }, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_loc_ret = apply_try_function(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None, target); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))); unwrap_suffixed_expression(arena, new_defs, None) } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { // TODO confirm this is correct with test case Err(EUnwrapped::Malformed) } @@ -769,6 +802,7 @@ fn unwrap_low_level_dbg<'a>( sub_arg, sub_pat, sub_new, + target, }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, @@ -777,18 +811,19 @@ fn unwrap_low_level_dbg<'a>( unwrap_suffixed_expression( arena, - apply_task_await( + apply_try_function( arena, new_dbg.region, sub_arg, sub_pat, new_dbg, None, + target, ), maybe_def_pat, ) } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!( "unreachable, arg of LowLevelDbg should generate UnwrappedSubExpr instead" ); @@ -805,17 +840,18 @@ fn unwrap_low_level_dbg<'a>( )); Ok(&*new_dbg) } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, LowLevelDbg(dbg_src, arg, unwrapped_expr), )); - Err(EUnwrapped::UnwrappedDefExpr(new_dbg)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_dbg, target }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, sub_pat, sub_new, + target, }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, @@ -825,6 +861,7 @@ fn unwrap_low_level_dbg<'a>( sub_arg: new_dbg, sub_pat, sub_new, + target, }) } Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), @@ -836,25 +873,26 @@ fn unwrap_low_level_dbg<'a>( } } -/// Helper for `Task.await loc_expr \loc_pat -> loc_cont` -pub fn apply_task_await<'a>( +/// Helper for try_function loc_expr \loc_pat -> loc_cont` +pub fn apply_try_function<'a>( arena: &'a Bump, region: Region, loc_expr: &'a Loc>, loc_pat: &'a Loc>, loc_cont: &'a Loc>, maybe_loc_ann: Option<(&'a Loc, &'a Loc>)>, + target: TryTarget, ) -> &'a Loc> { - let task_await_first_arg = match maybe_loc_ann { + let try_function_first_arg = match maybe_loc_ann { Some((loc_ann_pat, loc_type)) => { // loc_ann_pat : loc_type // loc_pat = loc_expr! // loc_cont // desugar to - // Task.await + // try_function // ( - // #!0_expr : Task loc_type _ + // #!0_expr : Target loc_type _ // #!0_expr = loc_expr // #!0_expr // ) @@ -875,8 +913,12 @@ pub fn apply_task_await<'a>( let new_ident = arena.alloc(new_ident); // #!0_expr (pattern) - // #!0_expr : Task loc_type _ + // #!0_expr : Target loc_type _ // #!0_expr = loc_expr + let target_type_name = match target { + TryTarget::Task => "Task", + TryTarget::Result => "Result", + }; let value_def = ValueDef::AnnotatedBody { ann_pattern: arena.alloc(Loc::at( loc_ann_pat.region, @@ -893,7 +935,7 @@ pub fn apply_task_await<'a>( loc_type.region, TypeAnnotation::Apply( arena.alloc(""), - arena.alloc("Task"), + arena.alloc(target_type_name), arena.alloc([ *loc_type, Loc::at(loc_type.region, TypeAnnotation::Inferred), @@ -918,7 +960,7 @@ pub fn apply_task_await<'a>( )); // ( - // #!0_expr : Task loc_type _ + // #!0_expr : Target loc_type _ // #!0_expr = loc_expr // #!0_expr // ) @@ -935,7 +977,7 @@ pub fn apply_task_await<'a>( // loc_cont // desugar to - // Task.await loc_expr \loc_pat -> loc_cont + // try_function loc_expr \loc_pat -> loc_cont loc_expr } }; @@ -945,25 +987,29 @@ pub fn apply_task_await<'a>( // \x -> x! // \x -> x if is_matching_intermediate_answer(loc_pat, loc_cont) { - return task_await_first_arg; + return try_function_first_arg; } // \loc_pat -> loc_cont let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont))); - // Task.await task_first_arg closure + // try_function first_arg closure + let (try_function_module, try_function_ident, called_via) = match target { + TryTarget::Task => (ModuleName::TASK, "await", CalledVia::BangSuffix), + TryTarget::Result => (ModuleName::RESULT, "try", CalledVia::QuestionSuffix), + }; arena.alloc(Loc::at( region, Apply( arena.alloc(Loc { region, value: Var { - module_name: ModuleName::TASK, - ident: "await", + module_name: try_function_module, + ident: try_function_ident, }, }), - arena.alloc([task_await_first_arg, closure]), - CalledVia::BangSuffix, + arena.alloc([try_function_first_arg, closure]), + called_via, ), )) } diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 04544ea2c6f..f61b19de4f0 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -9,8 +9,7 @@ use crate::spaces::{ use crate::Buf; use roc_module::called_via::{self, BinOp}; use roc_parse::ast::{ - is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, - OldRecordBuilderField, Pattern, WhenBranch, + is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, OldRecordBuilderField, Pattern, TryTarget, WhenBranch }; use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::ident::Accessor; @@ -47,7 +46,7 @@ impl<'a> Formattable for Expr<'a> { | OpaqueRef(_) | Crash => false, - RecordAccess(inner, _) | TupleAccess(inner, _) | TaskAwaitBang(inner) => { + RecordAccess(inner, _) | TupleAccess(inner, _) | TrySuffix { expr: inner, .. } => { inner.is_multiline() } @@ -520,9 +519,12 @@ impl<'a> Formattable for Expr<'a> { buf.push('.'); buf.push_str(key); } - TaskAwaitBang(expr) => { + TrySuffix { expr, target } => { expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); - buf.push('!'); + match target { + TryTarget::Task => buf.push('!'), + TryTarget::Result => buf.push('?'), + } } MalformedIdent(str, _) => { buf.indent(indent); diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index 43780ba9e15..cfac8d5f2a3 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -95,6 +95,10 @@ pub enum CalledVia { /// This call is the result of desugaring a Task.await from `!` syntax /// e.g. Stdout.line! "Hello" becomes Task.await (Stdout.line "Hello") \{} -> ... BangSuffix, + + /// This call is the result of desugaring a Result.try from `?` syntax + /// e.g. Dict.get? items "key" becomes Result.try (Dict.get items "key") \item -> ... + QuestionSuffix, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 445b7c5254b..a4a29b1282d 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -392,6 +392,15 @@ pub enum StrLiteral<'a> { Block(&'a [&'a [StrSegment<'a>]]), } +/// Values that can be tried, extracting success values or "returning early" on failure +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TryTarget { + /// Tasks suffixed with ! are `Task.await`ed + Task, + /// Results suffixed with ? are `Result.try`ed + Result, +} + /// A parsed expression. This uses lifetimes extensively for two reasons: /// /// 1. It uses Bump::alloc for all allocations, which returns a reference. @@ -426,8 +435,11 @@ pub enum Expr<'a> { /// Look up exactly one field on a tuple, e.g. `(x, y).1`. TupleAccess(&'a Expr<'a>, &'a str), - /// Task await bang - i.e. the ! in `File.readUtf8! path` - TaskAwaitBang(&'a Expr<'a>), + /// Early return on failures - e.g. the ! in `File.readUtf8! path` + TrySuffix { + target: TryTarget, + expr: &'a Expr<'a>, + }, // Collection Literals List(Collection<'a, &'a Loc>>), @@ -555,9 +567,9 @@ pub fn split_loc_exprs_around<'a>( /// Checks if the bang suffix is applied only at the top level of expression pub fn is_top_level_suffixed(expr: &Expr) -> bool { - // TODO: should we check BinOps with pizza where the last expression is TaskAwaitBang? + // TODO: should we check BinOps with pizza where the last expression is TrySuffix? match expr { - Expr::TaskAwaitBang(..) => true, + Expr::TrySuffix { .. } => true, Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value), Expr::SpaceBefore(a, _) => is_top_level_suffixed(a), Expr::SpaceAfter(a, _) => is_top_level_suffixed(a), @@ -571,7 +583,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool { // expression without arguments, `read!` Expr::Var { .. } => false, - Expr::TaskAwaitBang(..) => true, + Expr::TrySuffix { .. } => true, // expression with arguments, `line! "Foo"` Expr::Apply(sub_loc_expr, apply_args, _) => { @@ -995,7 +1007,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { } RecordAccess(expr, _) | TupleAccess(expr, _) - | TaskAwaitBang(expr) + | TrySuffix { expr, .. } | SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => expr_stack.push(expr), @@ -2486,7 +2498,7 @@ impl<'a> Malformed for Expr<'a> { RecordAccess(inner, _) | TupleAccess(inner, _) | - TaskAwaitBang(inner) => inner.is_malformed(), + TrySuffix { expr: inner, .. } => inner.is_malformed(), List(items) => items.is_malformed(), diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index d19fc740ec7..0603a5d88b5 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,9 +1,5 @@ use crate::ast::{ - is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, - Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, - ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, - ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, - TypeAnnotation, TypeDef, TypeHeader, ValueDef, + is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef }; use crate::blankspace::{ loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, @@ -169,7 +165,8 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), - map(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang), + map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Task)), + map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Result)), )) } @@ -2177,7 +2174,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result return Err(()), @@ -3749,7 +3746,10 @@ fn apply_expr_access_chain<'a>( Suffix::Accessor(Accessor::TupleIndex(field)) => { Expr::TupleAccess(arena.alloc(value), field) } - Suffix::TaskAwaitBang => Expr::TaskAwaitBang(arena.alloc(value)), + Suffix::TrySuffix(target) => Expr::TrySuffix { + target, + expr: arena.alloc(value), + }, }) } diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 8b28bb2ffa0..58a1bb3d39a 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -1,3 +1,4 @@ +use crate::ast::TryTarget; use crate::parser::Progress::{self, *}; use crate::parser::{BadInputError, EExpr, ParseResult, Parser}; use crate::state::State; @@ -377,7 +378,7 @@ impl<'a> Accessor<'a> { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Suffix<'a> { Accessor(Accessor<'a>), - TaskAwaitBang, + TrySuffix(TryTarget), } /// a `.foo` or `.1` accessor function diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 3d69dc36571..709a9e24290 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -722,7 +722,7 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.normalize(arena)), b), Expr::AccessorFunction(a) => Expr::AccessorFunction(a), Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), - Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.normalize(arena))), + Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { expr: arena.alloc(a.normalize(arena)), target }, Expr::List(a) => Expr::List(a.normalize(arena)), Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { update: arena.alloc(update.normalize(arena)), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast index 5b346e9c0cc..aa154180b73 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast @@ -62,12 +62,13 @@ Defs { ), Stmt( @97-111 Apply( - @97-108 TaskAwaitBang( - Var { + @97-108 TrySuffix { + target: Task, + expr: Var { module_name: "Stdout", ident: "line", }, - ), + }, [ @110-111 Var { module_name: "", @@ -80,12 +81,13 @@ Defs { ], }, @141-150 SpaceBefore( - TaskAwaitBang( - Var { + TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "printBar", }, - ), + }, [ Newline, Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast index 45b2889f076..29e7d578060 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast @@ -26,12 +26,13 @@ Defs { ( @11-23 SpaceAfter( Apply( - @11-19 TaskAwaitBang( - Var { + @11-19 TrySuffix { + target: Task, + expr: Var { module_name: "Arg", ident: "list", }, - ), + }, [ @21-23 Record( [], @@ -88,12 +89,13 @@ Defs { ), ( @83-98 SpaceAfter( - TaskAwaitBang( - Var { + TrySuffix { + target: Task, + expr: Var { module_name: "Task", ident: "fromResult", }, - ), + }, [ Newline, ], @@ -152,12 +154,13 @@ Defs { @174-176 Pizza, ), ], - @177-188 TaskAwaitBang( - Var { + @177-188 TrySuffix { + target: Task, + expr: Var { module_name: "Stdout", ident: "line", }, - ), + }, ), [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast deleted file mode 100644 index 1c715e9505e..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast +++ /dev/null @@ -1,10 +0,0 @@ -TaskAwaitBang( - TaskAwaitBang( - TaskAwaitBang( - Var { - module_name: "Stdout", - ident: "line", - }, - ), - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast new file mode 100644 index 00000000000..f75bc40a558 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast @@ -0,0 +1,13 @@ +TrySuffix { + target: Task, + expr: TrySuffix { + target: Task, + expr: TrySuffix { + target: Task, + expr: Var { + module_name: "Stdout", + ident: "line", + }, + }, + }, +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast similarity index 86% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast index aa86b9b304a..5d986547d54 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast @@ -44,12 +44,13 @@ Defs { value_defs: [ Stmt( @12-21 Apply( - @12-13 TaskAwaitBang( - Var { + @12-13 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "a", }, - ), + }, [ @16-21 Str( PlainLine( @@ -65,12 +66,13 @@ Defs { ident: "x", }, @29-39 Apply( - @29-32 TaskAwaitBang( - Var { + @29-32 TrySuffix { + target: Task, + expr: Var { module_name: "B", ident: "b", }, - ), + }, [ @34-39 Str( PlainLine( @@ -85,12 +87,13 @@ Defs { }, @45-49 SpaceBefore( Apply( - @45-46 TaskAwaitBang( - Var { + @45-46 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "c", }, - ), + }, [ @48-49 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast similarity index 81% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast index 368792b05ca..e62e74bd7fe 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast @@ -1,19 +1,21 @@ Apply( - @0-3 TaskAwaitBang( - Var { + @0-3 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "foo", }, - ), + }, [ @9-17 ParensAround( Apply( - @9-12 TaskAwaitBang( - Var { + @9-12 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "bar", }, - ), + }, [ @14-17 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast similarity index 94% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast index b124b30b99f..d74a5bb54f8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast @@ -123,12 +123,13 @@ Full( @126-128 Pizza, ), ], - @129-132 TaskAwaitBang( - Var { + @129-132 TrySuffix { + target: Task, + expr: Var { module_name: "A", ident: "x", }, - ), + }, ), ), Stmt( @@ -144,12 +145,13 @@ Full( ), ], @171-205 Apply( - @171-174 TaskAwaitBang( - Var { + @171-174 TrySuffix { + target: Task, + expr: Var { module_name: "B", ident: "y", }, - ), + }, [ @185-205 SpaceBefore( Record( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast similarity index 96% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast index 78f12e6ffd3..31cf2f05ec5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast @@ -108,12 +108,13 @@ Full( ), ], @167-202 Apply( - @167-178 TaskAwaitBang( - Var { + @167-178 TrySuffix { + target: Task, + expr: Var { module_name: "Task", ident: "mapErr", }, - ), + }, [ @180-202 Tag( "UnableToCheckJQVersion", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast new file mode 100644 index 00000000000..fd56db822d4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast @@ -0,0 +1,13 @@ +TrySuffix { + target: Result, + expr: TrySuffix { + target: Result, + expr: TrySuffix { + target: Result, + expr: Var { + module_name: "Stdout", + ident: "line", + }, + }, + }, +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc new file mode 100644 index 00000000000..4382b519a6f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc @@ -0,0 +1 @@ +Stdout.line??? \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc new file mode 100644 index 00000000000..62c98cbff83 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc @@ -0,0 +1,5 @@ +main = + a? "Bar" + x = B.b? "Foo" + + c? x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast new file mode 100644 index 00000000000..8e5f0739009 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast @@ -0,0 +1,119 @@ +Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-49, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-4 Identifier { + ident: "main", + }, + @12-49 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @12-21, + @26-39, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Stmt( + @12-21 Apply( + @12-13 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "a", + }, + }, + [ + @16-21 Str( + PlainLine( + "Bar", + ), + ), + ], + Space, + ), + ), + Body( + @26-27 Identifier { + ident: "x", + }, + @29-39 Apply( + @29-32 TrySuffix { + target: Result, + expr: Var { + module_name: "B", + ident: "b", + }, + }, + [ + @34-39 Str( + PlainLine( + "Foo", + ), + ), + ], + Space, + ), + ), + ], + }, + @45-49 SpaceBefore( + Apply( + @45-46 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "c", + }, + }, + [ + @48-49 Var { + module_name: "", + ident: "x", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc new file mode 100644 index 00000000000..15400112ae7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc @@ -0,0 +1,5 @@ +main = + a? "Bar" + x= B.b? "Foo" + + c? x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc new file mode 100644 index 00000000000..cdf5e420252 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc @@ -0,0 +1 @@ +foo? (bar? baz) (blah stuff) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast new file mode 100644 index 00000000000..a6eaa3d4d30 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast @@ -0,0 +1,45 @@ +Apply( + @0-3 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "foo", + }, + }, + [ + @9-17 ParensAround( + Apply( + @9-12 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "bar", + }, + }, + [ + @14-17 Var { + module_name: "", + ident: "baz", + }, + ], + Space, + ), + ), + @22-32 ParensAround( + Apply( + @22-26 Var { + module_name: "", + ident: "blah", + }, + [ + @27-32 Var { + module_name: "", + ident: "stuff", + }, + ], + Space, + ), + ), + ], + Space, +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc new file mode 100644 index 00000000000..d25e7f53910 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc @@ -0,0 +1 @@ +foo? ( bar? baz) ( blah stuff) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc new file mode 100644 index 00000000000..e0bc1818e9b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc @@ -0,0 +1,17 @@ +app [main] { + cli: "../basic-cli/platform/main.roc", +} + +import cli.Stdout + +main = + # is this a valid statement? + "Foo" |> A.x? + + # what about this? + "Bar" + |> B.y? + { config: "config" } + + C.z "Bar" + diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast new file mode 100644 index 00000000000..da9d4a5b6a7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast @@ -0,0 +1,213 @@ +Full( + FullAst { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [], + provides: [ + @5-9 ExposedName( + "main", + ), + ], + before_packages: [], + packages: @11-55 Collection { + items: [ + @15-52 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: None, + package_name: @20-52 PackageName( + "../basic-cli/platform/main.roc", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + old_imports: None, + old_provides_to_new_package: None, + }, + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @57-74, + @76-220, + ], + space_before: [ + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 2, length = 0), + Slice(start = 4, length = 2), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + ModuleImport( + ModuleImport { + before_name: [], + name: @64-74 ImportedModuleName { + package: Some( + "cli", + ), + name: ModuleName( + "Stdout", + ), + }, + params: None, + alias: None, + exposed: None, + }, + ), + Body( + @76-80 Identifier { + ident: "main", + }, + @120-220 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @120-133, + @162-205, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 3), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 3, length = 0), + ], + spaces: [ + Newline, + Newline, + LineComment( + " what about this?", + ), + ], + type_defs: [], + value_defs: [ + Stmt( + @120-133 BinOps( + [ + ( + @120-125 Str( + PlainLine( + "Foo", + ), + ), + @126-128 Pizza, + ), + ], + @129-132 TrySuffix { + target: Result, + expr: Var { + module_name: "A", + ident: "x", + }, + }, + ), + ), + Stmt( + @162-205 BinOps( + [ + ( + @162-167 Str( + PlainLine( + "Bar", + ), + ), + @168-170 Pizza, + ), + ], + @171-205 Apply( + @171-174 TrySuffix { + target: Result, + expr: Var { + module_name: "B", + ident: "y", + }, + }, + [ + @185-205 SpaceBefore( + Record( + [ + @187-203 RequiredValue( + @187-193 "config", + [], + @195-203 Str( + PlainLine( + "config", + ), + ), + ), + ], + ), + [ + Newline, + ], + ), + ], + Space, + ), + ), + ), + ], + }, + @211-220 SpaceBefore( + Apply( + @211-214 Var { + module_name: "C", + ident: "z", + }, + [ + @215-220 Str( + PlainLine( + "Bar", + ), + ), + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + LineComment( + " is this a valid statement?", + ), + ], + ), + ), + ], + }, + }, +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc new file mode 100644 index 00000000000..177c84c4361 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc @@ -0,0 +1,16 @@ +app [main] { + cli: "../basic-cli/platform/main.roc", +} + +import cli.Stdout + +main = + # is this a valid statement? + "Foo" |> A.x? + + # what about this? + "Bar" |> B.y? + { config: "config" } + + C.z "Bar" + diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc new file mode 100644 index 00000000000..65fc305940a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc @@ -0,0 +1,9 @@ +app [main] { + cli: platform "", +} + +main = + "jq --version" + |> Cmd.new + |> Cmd.status + |> Result.mapErr? UnableToCheckJQVersion diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast new file mode 100644 index 00000000000..9bef33589c9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast @@ -0,0 +1,134 @@ +Full( + FullAst { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [ + Newline, + ], + provides: [ + @74-78 ExposedName( + "main", + ), + ], + before_packages: [ + Newline, + ], + packages: @6-44 Collection { + items: [ + @30-37 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: Some( + [], + ), + package_name: @35-37 PackageName( + "", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + old_imports: None, + old_provides_to_new_package: None, + }, + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @88-204, + ], + space_before: [ + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @88-92 Identifier { + ident: "main", + }, + @100-204 SpaceBefore( + BinOps( + [ + ( + @100-114 SpaceAfter( + Str( + PlainLine( + "jq --version", + ), + ), + [ + Newline, + ], + ), + @123-125 Pizza, + ), + ( + @126-133 SpaceAfter( + Var { + module_name: "Cmd", + ident: "new", + }, + [ + Newline, + ], + ), + @142-144 Pizza, + ), + ( + @145-155 SpaceAfter( + Var { + module_name: "Cmd", + ident: "status", + }, + [ + Newline, + ], + ), + @164-166 Pizza, + ), + ], + @167-204 Apply( + @167-180 TrySuffix { + target: Result, + expr: Var { + module_name: "Result", + ident: "mapErr", + }, + }, + [ + @182-204 Tag( + "UnableToCheckJQVersion", + ), + ], + Space, + ), + ), + [ + Newline, + ], + ), + ), + ], + }, + }, +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc new file mode 100644 index 00000000000..a2b949f35e0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc @@ -0,0 +1,12 @@ +app "" + packages { + cli: "", + } + imports [] + provides [main] to cli + +main = + "jq --version" + |> Cmd.new + |> Cmd.status + |> Result.mapErr? UnableToCheckJQVersion diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 44fb528d4cb..0ad5e2bb65f 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -469,11 +469,16 @@ mod test_snapshots { pass/string_without_escape.expr, pass/sub_var_with_spaces.expr, pass/sub_with_spaces.expr, - pass/suffixed.expr, - pass/suffixed_multiple_defs.moduledefs, - pass/suffixed_nested.expr, - pass/suffixed_one_def.full, - pass/suffixed_optional_last.full, + pass/suffixed_bang.expr, + pass/suffixed_bang_multiple_defs.moduledefs, + pass/suffixed_bang_nested.expr, + pass/suffixed_bang_one_def.full, + pass/suffixed_bang_optional_last.full, + pass/suffixed_question.expr, + pass/suffixed_question_multiple_defs.moduledefs, + pass/suffixed_question_nested.expr, + pass/suffixed_question_one_def.full, + pass/suffixed_question_optional_last.full, pass/tag_pattern.expr, pass/ten_times_eleven.expr, pass/three_arg_closure.expr, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 394b028bd91..18514583258 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -667,7 +667,7 @@ impl IterTokens for Loc> { Expr::RecordAccess(rcd, _field) => Loc::at(region, *rcd).iter_tokens(arena), Expr::AccessorFunction(accessor) => Loc::at(region, accessor).iter_tokens(arena), Expr::TupleAccess(tup, _field) => Loc::at(region, *tup).iter_tokens(arena), - Expr::TaskAwaitBang(inner) => Loc::at(region, *inner).iter_tokens(arena), + Expr::TrySuffix { expr: inner, .. } => Loc::at(region, *inner).iter_tokens(arena), Expr::List(lst) => lst.iter_tokens(arena), Expr::RecordUpdate { update, fields } => (update.iter_tokens(arena).into_iter()) .chain(fields.iter().flat_map(|f| f.iter_tokens(arena))) From 4870f56a1823b7fac0764134ba4579216b71d24a Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 22:21:53 -0700 Subject: [PATCH 178/203] Update args.roc test to use ? sugar --- crates/cli/tests/cli/parse-args.roc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/cli/tests/cli/parse-args.roc b/crates/cli/tests/cli/parse-args.roc index 556367b1e17..c9bb274f99c 100644 --- a/crates/cli/tests/cli/parse-args.roc +++ b/crates/cli/tests/cli/parse-args.roc @@ -58,8 +58,7 @@ cliMap : ArgParser a, (a -> b) -> ArgParser b cliMap = \{ params, parser }, mapper -> { params, parser: \args -> - (data, afterData) <- parser args - |> Result.try + (data, afterData) = parser? args Ok (mapper data, afterData), } @@ -68,10 +67,8 @@ cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c cliBuild = \firstWeaver, secondWeaver, combine -> allParams = List.concat firstWeaver.params secondWeaver.params combinedParser = \args -> - (firstValue, afterFirst) <- firstWeaver.parser args - |> Result.try - (secondValue, afterSecond) <- secondWeaver.parser afterFirst - |> Result.try + (firstValue, afterFirst) = firstWeaver.parser? args + (secondValue, afterSecond) = secondWeaver.parser? afterFirst Ok (combine firstValue secondValue, afterSecond) From 0fd0cc11aa35a3928bdbe73c7485466241e81cf4 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 22:26:32 -0700 Subject: [PATCH 179/203] Format code --- crates/compiler/can/src/expr.rs | 13 ++++---- crates/compiler/can/src/suffixed.rs | 41 +++++++++++++++++++------- crates/compiler/fmt/src/expr.rs | 3 +- crates/compiler/parse/src/expr.rs | 14 +++++++-- crates/compiler/parse/src/normalize.rs | 5 +++- 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 9dd672fdf54..cceb62d910f 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -648,9 +648,7 @@ pub fn canonicalize_expr<'a>( (answer, Output::default()) } - ast::Expr::Record(fields) => { - canonicalize_record(env, var_store, scope, region, *fields) - } + ast::Expr::Record(fields) => canonicalize_record(env, var_store, scope, region, *fields), ast::Expr::RecordUpdate { fields, update: loc_update, @@ -1119,7 +1117,9 @@ pub fn canonicalize_expr<'a>( output, ) } - ast::Expr::TrySuffix { .. } => internal_error!("a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"), + ast::Expr::TrySuffix { .. } => internal_error!( + "a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed" + ), ast::Expr::Tag(tag) => { let variant_var = var_store.fresh(); let ext_var = var_store.fresh(); @@ -1371,7 +1371,10 @@ pub fn canonicalize_expr<'a>( use roc_problem::can::RuntimeError::*; let sub_region = Region::span_across(&loc_name.region, &loc_value.region); - let problem = OptionalFieldInRecordBuilder {record: region, field: sub_region }; + let problem = OptionalFieldInRecordBuilder { + record: region, + field: sub_region, + }; env.problem(Problem::RuntimeError(problem.clone())); (RuntimeError(problem), Output::default()) diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 251ec683ae9..81a717fcb65 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -71,7 +71,10 @@ fn init_unwrapped_err<'a>( Some(..) => { // we have a def pattern, so no need to generate a new pattern // as this should only be created in the first call from a def - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: unwrapped_expr, + target, + }) } None => { // Provide an intermediate answer expression and pattern when unwrapping a @@ -165,10 +168,16 @@ pub fn unwrap_suffixed_expression<'a>( .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); return Ok(new_expect); } - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target, }) => { + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: unwrapped_expr, + target, + }) => { let new_expect = arena .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_expect, target }) + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: new_expect, + target, + }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, @@ -330,9 +339,8 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => { - let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via))); - + Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target }) } Err(err) => Err(err) @@ -536,8 +544,15 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( sub_new, target, }) => { - let unwrapped_final_else = - apply_try_function(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None, target); + let unwrapped_final_else = apply_try_function( + arena, + sub_arg.region, + sub_arg, + sub_pat, + sub_new, + None, + target, + ); let new_if = arena.alloc(Loc::at( loc_expr.region, @@ -673,7 +688,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( return unwrap_suffixed_expression( arena, apply_try_function( - arena, + arena, def_expr.region, unwrapped_expr, def_pattern, @@ -840,12 +855,18 @@ fn unwrap_low_level_dbg<'a>( )); Ok(&*new_dbg) } - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: unwrapped_expr, + target, + }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, LowLevelDbg(dbg_src, arg, unwrapped_expr), )); - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_dbg, target }) + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: new_dbg, + target, + }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index f61b19de4f0..645b258a617 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -9,7 +9,8 @@ use crate::spaces::{ use crate::Buf; use roc_module::called_via::{self, BinOp}; use roc_parse::ast::{ - is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, OldRecordBuilderField, Pattern, TryTarget, WhenBranch + is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, + OldRecordBuilderField, Pattern, TryTarget, WhenBranch, }; use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::ident::Accessor; diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 0603a5d88b5..26b8c85dbdc 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,5 +1,9 @@ use crate::ast::{ - is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef + is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, + Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, + ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, + ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, + TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef, }; use crate::blankspace::{ loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, @@ -165,8 +169,12 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), - map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Task)), - map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Result)), + map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix( + TryTarget::Task + )), + map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix( + TryTarget::Result + )), )) } diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 709a9e24290..4f37a1e18ee 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -722,7 +722,10 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.normalize(arena)), b), Expr::AccessorFunction(a) => Expr::AccessorFunction(a), Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), - Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { expr: arena.alloc(a.normalize(arena)), target }, + Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { + expr: arena.alloc(a.normalize(arena)), + target, + }, Expr::List(a) => Expr::List(a.normalize(arena)), Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { update: arena.alloc(update.normalize(arena)), From 0733863a81fcf399a2fbe27d15c73183594de455 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 23:23:45 -0700 Subject: [PATCH 180/203] Prefer false-interpreter for ? desugaring example --- crates/cli/tests/cli/parse-args.roc | 11 ++++-- examples/cli/false-interpreter/False.roc | 48 ++++++++++++------------ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/crates/cli/tests/cli/parse-args.roc b/crates/cli/tests/cli/parse-args.roc index c9bb274f99c..145a2e36e47 100644 --- a/crates/cli/tests/cli/parse-args.roc +++ b/crates/cli/tests/cli/parse-args.roc @@ -12,7 +12,7 @@ main = file, count: numParam { name: "count" }, doubled: numParam { name: "doubled" } - |> cliMap \d -> d * 2, + |> cliMap \d -> d * 2, } args = ["parse-args", "file.txt", "5", "7"] @@ -58,7 +58,8 @@ cliMap : ArgParser a, (a -> b) -> ArgParser b cliMap = \{ params, parser }, mapper -> { params, parser: \args -> - (data, afterData) = parser? args + (data, afterData) <- parser args + |> Result.try Ok (mapper data, afterData), } @@ -67,8 +68,10 @@ cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c cliBuild = \firstWeaver, secondWeaver, combine -> allParams = List.concat firstWeaver.params secondWeaver.params combinedParser = \args -> - (firstValue, afterFirst) = firstWeaver.parser? args - (secondValue, afterSecond) = secondWeaver.parser? afterFirst + (firstValue, afterFirst) <- firstWeaver.parser args + |> Result.try + (secondValue, afterSecond) <- secondWeaver.parser afterFirst + |> Result.try Ok (combine firstValue secondValue, afterSecond) diff --git a/examples/cli/false-interpreter/False.roc b/examples/cli/false-interpreter/False.roc index 4c59516c1db..7300abff728 100644 --- a/examples/cli/false-interpreter/False.roc +++ b/examples/cli/false-interpreter/False.roc @@ -242,13 +242,13 @@ interpretCtxLoop = \ctx -> when result is Ok (T 0xB8 newCtx) -> result2 = - (T popCtx index) <- Result.try (popNumber newCtx) + (T popCtx index) = popNumber? newCtx # I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers. size = List.len popCtx.stack - 1 offset = Num.intCast size - index if offset >= 0 then - stackVal <- Result.try (List.get popCtx.stack (Num.intCast offset)) + stackVal = List.get? popCtx.stack (Num.intCast offset) Ok (Context.pushStack popCtx stackVal) else Err OutOfBounds @@ -292,7 +292,7 @@ stepExecCtx = \ctx, char -> # `!` execute lambda Task.fromResult ( - (T popCtx bytes) <- Result.try (popLambda ctx) + (T popCtx bytes) = popLambda? ctx Ok { popCtx & scopes: List.append popCtx.scopes { data: None, buf: bytes, index: 0, whileInfo: None } } ) @@ -300,8 +300,8 @@ stepExecCtx = \ctx, char -> # `?` if Task.fromResult ( - (T popCtx1 bytes) <- Result.try (popLambda ctx) - (T popCtx2 n1) <- Result.try (popNumber popCtx1) + (T popCtx1 bytes) = popLambda? ctx + (T popCtx2 n1) = popNumber? popCtx1 if n1 == 0 then Ok popCtx2 else @@ -312,8 +312,8 @@ stepExecCtx = \ctx, char -> # `#` while Task.fromResult ( - (T popCtx1 body) <- Result.try (popLambda ctx) - (T popCtx2 cond) <- Result.try (popLambda popCtx1) + (T popCtx1 body) = popLambda? ctx + (T popCtx2 cond) = popLambda? popCtx1 last = (List.len popCtx2.scopes - 1) when List.get popCtx2.scopes last is @@ -346,8 +346,8 @@ stepExecCtx = \ctx, char -> 0x5C -> # `\` swap result2 = - (T popCtx1 n1) <- Result.try (Context.popStack ctx) - (T popCtx2 n2) <- Result.try (Context.popStack popCtx1) + (T popCtx1 n1) = Context.popStack? ctx + (T popCtx2 n2) = Context.popStack? popCtx1 Ok (Context.pushStack (Context.pushStack popCtx2 n1) n2) when result2 is @@ -361,9 +361,9 @@ stepExecCtx = \ctx, char -> 0x40 -> # `@` rot result2 = - (T popCtx1 n1) <- Result.try (Context.popStack ctx) - (T popCtx2 n2) <- Result.try (Context.popStack popCtx1) - (T popCtx3 n3) <- Result.try (Context.popStack popCtx2) + (T popCtx1 n1) = Context.popStack? ctx + (T popCtx2 n2) = Context.popStack? popCtx1 + (T popCtx3 n3) = Context.popStack? popCtx2 Ok (Context.pushStack (Context.pushStack (Context.pushStack popCtx3 n2) n1) n3) when result2 is @@ -384,13 +384,13 @@ stepExecCtx = \ctx, char -> # `O` also treat this as pick for easier script writing Task.fromResult ( - (T popCtx index) <- Result.try (popNumber ctx) + (T popCtx index) = popNumber? ctx # I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers. size = List.len popCtx.stack - 1 offset = Num.intCast size - index if offset >= 0 then - stackVal <- Result.try (List.get popCtx.stack (Num.intCast offset)) + stackVal = List.get? popCtx.stack (Num.intCast offset) Ok (Context.pushStack popCtx stackVal) else Err OutOfBounds @@ -422,9 +422,9 @@ stepExecCtx = \ctx, char -> # Due to possible division by zero error, this must be handled specially. Task.fromResult ( - (T popCtx1 numR) <- Result.try (popNumber ctx) - (T popCtx2 numL) <- Result.try (popNumber popCtx1) - res <- Result.try (Num.divTruncChecked numL numR) + (T popCtx1 numR) = popNumber? ctx + (T popCtx2 numL) = popNumber? popCtx1 + res = Num.divTruncChecked? numL numR Ok (Context.pushStack popCtx2 (Number res)) ) @@ -504,9 +504,9 @@ stepExecCtx = \ctx, char -> # `:` store to variable Task.fromResult ( - (T popCtx1 var) <- Result.try (popVariable ctx) + (T popCtx1 var) = popVariable? ctx # The Result.mapErr on the next line maps from EmptyStack in Context.roc to the full InterpreterErrors union here. - (T popCtx2 n1) <- Result.try (Result.mapErr (Context.popStack popCtx1) (\EmptyStack -> EmptyStack)) + (T popCtx2 n1) = Result.mapErr? (Context.popStack popCtx1) (\EmptyStack -> EmptyStack) Ok { popCtx2 & vars: List.set popCtx2.vars (Variable.toIndex var) n1 } ) @@ -514,8 +514,8 @@ stepExecCtx = \ctx, char -> # `;` load from variable Task.fromResult ( - (T popCtx var) <- Result.try (popVariable ctx) - elem <- Result.try (List.get popCtx.vars (Variable.toIndex var)) + (T popCtx var) = popVariable? ctx + elem = List.get? popCtx.vars (Variable.toIndex var) Ok (Context.pushStack popCtx elem) ) @@ -551,13 +551,13 @@ stepExecCtx = \ctx, char -> unaryOp : Context, (I32 -> I32) -> Result Context InterpreterErrors unaryOp = \ctx, op -> - (T popCtx num) <- Result.try (popNumber ctx) + (T popCtx num) = popNumber? ctx Ok (Context.pushStack popCtx (Number (op num))) binaryOp : Context, (I32, I32 -> I32) -> Result Context InterpreterErrors binaryOp = \ctx, op -> - (T popCtx1 numR) <- Result.try (popNumber ctx) - (T popCtx2 numL) <- Result.try (popNumber popCtx1) + (T popCtx1 numR) = popNumber? ctx + (T popCtx2 numL) = popNumber? popCtx1 Ok (Context.pushStack popCtx2 (Number (op numL numR))) popNumber : Context -> Result [T Context I32] InterpreterErrors From fc5a53a08aec8e8d0f8680ed4d42a8907f5b6052 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 23:26:42 -0700 Subject: [PATCH 181/203] Enable false-interpreter test --- crates/cli/tests/cli_run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 25237266f28..7eb215a554a 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -892,7 +892,6 @@ mod cli_run { } #[test] - #[cfg_attr(any(target_os = "windows", target_os = "linux"), ignore = "Segfault")] fn false_interpreter() { test_roc_app( "examples/cli/false-interpreter", From fdacdbe5efd414d0e4949674eb8b00f60d44df3f Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Thu, 15 Aug 2024 07:55:02 -0700 Subject: [PATCH 182/203] Re-ignore false interpreter test on Windows --- crates/cli/tests/cli_run.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 7eb215a554a..9904498c1e2 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -892,6 +892,7 @@ mod cli_run { } #[test] + #[cfg_attr(target_os = "windows", ignore = "Segfault")] fn false_interpreter() { test_roc_app( "examples/cli/false-interpreter", From b1a972ab219497098898d3e4c8252b702d354644 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 01:41:59 -0700 Subject: [PATCH 183/203] Implement `&foo` record updater syntax sugar --- crates/cli/tests/benchmarks/AStar.roc | 5 +- crates/compiler/can/src/debug/pretty_print.rs | 9 +- crates/compiler/can/src/desugar.rs | 53 ++++++++++++ crates/compiler/can/src/expr.rs | 4 + crates/compiler/fmt/src/expr.rs | 6 ++ crates/compiler/parse/src/ast.rs | 6 ++ crates/compiler/parse/src/expr.rs | 4 +- crates/compiler/parse/src/ident.rs | 55 +++++------- crates/compiler/parse/src/normalize.rs | 3 + crates/compiler/parse/src/parser.rs | 1 + crates/compiler/parse/src/pattern.rs | 4 + crates/compiler/test_gen/src/gen_records.rs | 32 +++++++ ...d_updater_literal_apply.expr.formatted.roc | 5 ++ ...cord_updater_literal_apply.expr.result-ast | 86 +++++++++++++++++++ .../record_updater_literal_apply.expr.roc | 5 ++ ...ecord_updater_var_apply.expr.formatted.roc | 1 + .../record_updater_var_apply.expr.result-ast | 20 +++++ .../pass/record_updater_var_apply.expr.roc | 1 + .../test_syntax/tests/test_snapshots.rs | 2 + crates/language_server/src/analysis/tokens.rs | 1 + crates/reporting/src/error/canonicalize.rs | 32 +++++++ crates/reporting/src/report.rs | 8 +- 22 files changed, 302 insertions(+), 41 deletions(-) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.roc diff --git a/crates/cli/tests/benchmarks/AStar.roc b/crates/cli/tests/benchmarks/AStar.roc index 14f4a42dbcf..acfaa26e076 100644 --- a/crates/cli/tests/benchmarks/AStar.roc +++ b/crates/cli/tests/benchmarks/AStar.roc @@ -92,9 +92,8 @@ astar = \costFn, moveFn, goal, model -> modelWithNeighbors : Model position modelWithNeighbors = - { modelPopped & - openSet: Set.union modelPopped.openSet newNeighbors, - } + modelPopped + |> &openSet (Set.union modelPopped.openSet newNeighbors) walker : Model position, position -> Model position walker = \amodel, n -> updateCost current n amodel diff --git a/crates/compiler/can/src/debug/pretty_print.rs b/crates/compiler/can/src/debug/pretty_print.rs index ad1f01f0ee4..b842d6076ef 100644 --- a/crates/compiler/can/src/debug/pretty_print.rs +++ b/crates/compiler/can/src/debug/pretty_print.rs @@ -3,12 +3,14 @@ use crate::def::Def; use crate::expr::Expr::{self, *}; use crate::expr::{ - ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, WhenBranch, + ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, + StructAccessorData, WhenBranch, }; use crate::pattern::{Pattern, RecordDestruct, TupleDestruct}; use roc_module::symbol::{Interns, ModuleId, Symbol}; +use roc_types::types::IndexOrField; use ven_pretty::{text, Arena, DocAllocator, DocBuilder}; pub struct Ctx<'a> { @@ -381,7 +383,10 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a, OpaqueWrapFunction(OpaqueWrapFunctionData { opaque_name, .. }) => { text!(f, "@{}", opaque_name.as_str(c.interns)) } - RecordAccessor(_) => todo!(), + RecordAccessor(StructAccessorData { field, .. }) => match field { + IndexOrField::Index(index) => text!(f, ".{}", index), + IndexOrField::Field(name) => text!(f, ".{}", name), + }, RecordUpdate { symbol, updates, .. } => f diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 75cc844a773..62f40c139db 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -476,6 +476,59 @@ pub fn desugar_expr<'a>( }, }) } + RecordUpdater(field_name) => { + let region = loc_expr.region; + + let closure_body = RecordUpdate { + update: arena.alloc(Loc { + region, + value: Expr::Var { + module_name: "", + ident: "#record_updater_record", + }, + }), + fields: Collection::with_items( + Vec::from_iter_in( + [ + Loc::at(region, AssignedField::RequiredValue( + Loc::at(region, field_name), + &[], + &*arena.alloc(Loc { + region, + value: Expr::Var { + module_name: "", + ident: "#record_updater_field", + }, + }), + )) + ], + arena, + ) + .into_bump_slice(), + ) + }; + + arena.alloc(Loc { + region, + value: Closure( + arena.alloc_slice_copy(&[ + Loc::at( + region, + Pattern::Identifier { + ident: "#record_updater_record", + }, + ), + Loc::at( + region, + Pattern::Identifier { + ident: "#record_updater_field", + }, + ), + ]), + arena.alloc(Loc::at(region, closure_body)), + ), + }) + } Closure(loc_patterns, loc_ret) => arena.alloc(Loc { region: loc_expr.region, value: Closure( diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index cceb62d910f..d08d6efd669 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1020,6 +1020,9 @@ pub fn canonicalize_expr<'a>( ast::Expr::Backpassing(_, _, _) => { internal_error!("Backpassing should have been desugared by now") } + ast::Expr::RecordUpdater(_) => { + internal_error!("Record updater should have been desugared by now") + } ast::Expr::Closure(loc_arg_patterns, loc_body_expr) => { let (closure_data, output) = canonicalize_closure(env, var_store, scope, loc_arg_patterns, loc_body_expr, None); @@ -2465,6 +2468,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { | ast::Expr::Num(_) | ast::Expr::NonBase10Int { .. } | ast::Expr::AccessorFunction(_) + | ast::Expr::RecordUpdater(_) | ast::Expr::Crash | ast::Expr::Underscore(_) | ast::Expr::MalformedIdent(_, _) diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 645b258a617..7d6a7ba37e1 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -39,6 +39,7 @@ impl<'a> Formattable for Expr<'a> { | NonBase10Int { .. } | SingleQuote(_) | AccessorFunction(_) + | RecordUpdater(_) | Var { .. } | Underscore { .. } | MalformedIdent(_, _) @@ -510,6 +511,11 @@ impl<'a> Formattable for Expr<'a> { Accessor::TupleIndex(key) => buf.push_str(key), } } + RecordUpdater(key) => { + buf.indent(indent); + buf.push('&'); + buf.push_str(key); + } RecordAccess(expr, key) => { expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); buf.push('.'); diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index a4a29b1282d..7ee687b19e1 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -432,6 +432,9 @@ pub enum Expr<'a> { /// e.g. `.foo` or `.0` AccessorFunction(Accessor<'a>), + /// Update the value of a field in a record, e.g. `&foo` + RecordUpdater(&'a str), + /// Look up exactly one field on a tuple, e.g. `(x, y).1`. TupleAccess(&'a Expr<'a>, &'a str), @@ -636,6 +639,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool { Expr::SingleQuote(_) => false, Expr::RecordAccess(a, _) => is_expr_suffixed(a), Expr::AccessorFunction(_) => false, + Expr::RecordUpdater(_) => false, Expr::TupleAccess(a, _) => is_expr_suffixed(a), Expr::List(items) => items.iter().any(|x| is_expr_suffixed(&x.value)), Expr::RecordUpdate { update, fields } => { @@ -1024,6 +1028,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { | Str(_) | SingleQuote(_) | AccessorFunction(_) + | RecordUpdater(_) | Var { .. } | Underscore(_) | Crash @@ -2487,6 +2492,7 @@ impl<'a> Malformed for Expr<'a> { Num(_) | NonBase10Int { .. } | AccessorFunction(_) | + RecordUpdater(_) | Var { .. } | Underscore(_) | Tag(_) | diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 26b8c85dbdc..dda94cce4f1 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -2181,6 +2181,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result( /// 2. The beginning of a function call (e.g. `foo bar baz`) /// 3. The beginning of a definition (e.g. `foo =`) /// 4. The beginning of a type annotation (e.g. `foo :`) -/// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else. +/// 5. A reserved keyword (e.g. `if ` or `when `), meaning we should do something else. fn assign_or_destructure_identifier<'a>() -> impl Parser<'a, Ident<'a>, EExpr<'a>> { parse_ident @@ -3313,6 +3314,7 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> { answer } Ident::AccessorFunction(string) => Expr::AccessorFunction(string), + Ident::RecordUpdaterFunction(string) => Expr::RecordUpdater(string), Ident::Malformed(string, problem) => Expr::MalformedIdent(string, problem), } } diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 58a1bb3d39a..9e9c2f5bc94 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -46,42 +46,12 @@ pub enum Ident<'a> { }, /// `.foo { foo: 42 }` or `.1 (1, 2, 3)` AccessorFunction(Accessor<'a>), + /// `&foo { foo: 42 } 3` + RecordUpdaterFunction(&'a str), /// .Foo or foo. or something like foo.Bar Malformed(&'a str, BadIdent), } -impl<'a> Ident<'a> { - pub fn len(&self) -> usize { - use self::Ident::*; - - match self { - Tag(string) | OpaqueRef(string) => string.len(), - Access { - module_name, parts, .. - } => { - let mut len = if module_name.is_empty() { - 0 - } else { - module_name.len() + 1 - // +1 for the dot - }; - - for part in parts.iter() { - len += part.len() + 1 // +1 for the dot - } - - len - 1 - } - AccessorFunction(string) => string.len(), - Malformed(string, _) => string.len(), - } - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - /// This could be: /// /// * A record field, e.g. "email" in `.email` or in `email:` @@ -272,6 +242,7 @@ pub enum BadIdent { WeirdDotAccess(Position), WeirdDotQualified(Position), StrayDot(Position), + StrayAmpersand(Position), BadOpaqueRef(Position), QualifiedTupleAccessor(Position), } @@ -416,6 +387,18 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result { } } +/// a `&foo` record updater function +fn chomp_record_updater(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> { + // assumes the leading `&` has been chomped already + match chomp_lowercase_part(buffer) { + Ok(name) => Ok(name), + Err(_) => { + // we've already made progress with the initial `&` + Err(BadIdent::StrayAmpersand(pos.bump_column(1))) + } + } +} + /// a `@Token` opaque fn chomp_opaque_ref(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> { // assumes the leading `@` has NOT been chomped already @@ -458,6 +441,14 @@ fn chomp_identifier_chain<'a>( } Err(fail) => return Err((1, fail)), }, + '&' => match chomp_record_updater(&buffer[1..], pos) { + Ok(updater) => { + let bytes_parsed = 1 + updater.len(); + return Ok((bytes_parsed as u32, Ident::RecordUpdaterFunction(updater))); + } + // return 0 bytes consumed on failure to allow parsing && + Err(fail) => return Err((0, fail)), + }, '@' => match chomp_opaque_ref(buffer, pos) { Ok(tagname) => { let bytes_parsed = tagname.len(); diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 4f37a1e18ee..62e840811dd 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -721,6 +721,7 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::Str(a) => Expr::Str(a.normalize(arena)), Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.normalize(arena)), b), Expr::AccessorFunction(a) => Expr::AccessorFunction(a), + Expr::RecordUpdater(a) => Expr::RecordUpdater(a), Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { expr: arena.alloc(a.normalize(arena)), @@ -840,6 +841,7 @@ fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent { BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()), BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()), BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()), + BadIdent::StrayAmpersand(_) => BadIdent::StrayAmpersand(Position::zero()), BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()), BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()), } @@ -1227,6 +1229,7 @@ impl<'a> Normalize<'a> for EPattern<'a> { EPattern::IndentEnd(_) => EPattern::IndentEnd(Position::zero()), EPattern::AsIndentStart(_) => EPattern::AsIndentStart(Position::zero()), EPattern::AccessorFunction(_) => EPattern::AccessorFunction(Position::zero()), + EPattern::RecordUpdaterFunction(_) => EPattern::RecordUpdaterFunction(Position::zero()), } } } diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index c773acab730..75ec4066bb8 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -605,6 +605,7 @@ pub enum EPattern<'a> { AsIndentStart(Position), AccessorFunction(Position), + RecordUpdaterFunction(Position), } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/compiler/parse/src/pattern.rs b/crates/compiler/parse/src/pattern.rs index 623dc2e872d..74e443a5157 100644 --- a/crates/compiler/parse/src/pattern.rs +++ b/crates/compiler/parse/src/pattern.rs @@ -436,6 +436,10 @@ fn loc_ident_pattern_help<'a>( MadeProgress, EPattern::AccessorFunction(loc_ident.region.start()), )), + Ident::RecordUpdaterFunction(_string) => Err(( + MadeProgress, + EPattern::RecordUpdaterFunction(loc_ident.region.start()), + )), Ident::Malformed(malformed, problem) => { debug_assert!(!malformed.is_empty()); diff --git a/crates/compiler/test_gen/src/gen_records.rs b/crates/compiler/test_gen/src/gen_records.rs index 8454c091bcd..23552198420 100644 --- a/crates/compiler/test_gen/src/gen_records.rs +++ b/crates/compiler/test_gen/src/gen_records.rs @@ -885,6 +885,38 @@ fn update_single_element_record() { ); } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn update_record_shorthand() { + assert_evals_to!( + indoc!( + r" + rec = { foo: 42, bar: 2.46f64 } + + rec |> &foo (rec.foo + 1) + " + ), + (2.46, 43), + (f64, i64) + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn update_single_element_record_shorthand() { + assert_evals_to!( + indoc!( + r" + rec = { foo: 42} + + &foo rec (rec.foo + 1) + " + ), + 43, + i64 + ); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn booleans_in_record() { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.formatted.roc new file mode 100644 index 00000000000..22be633a129 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.formatted.roc @@ -0,0 +1,5 @@ +data = + { x: 5, y: 0 } + |> &y 3 + +data \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast new file mode 100644 index 00000000000..c19e780dbaf --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast @@ -0,0 +1,86 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-42, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-4 Identifier { + ident: "data", + }, + @11-42 SpaceBefore( + BinOps( + [ + ( + @11-25 SpaceAfter( + Record( + [ + @13-17 RequiredValue( + @13-14 "x", + [], + @16-17 Num( + "5", + ), + ), + @19-23 RequiredValue( + @19-20 "y", + [], + @22-23 Num( + "0", + ), + ), + ], + ), + [ + Newline, + ], + ), + @34-36 Pizza, + ), + ], + @37-42 Apply( + @37-39 RecordUpdater( + "y", + ), + [ + @41-42 Num( + "3", + ), + ], + Space, + ), + ), + [ + Newline, + ], + ), + ), + ], + }, + @44-48 SpaceBefore( + Var { + module_name: "", + ident: "data", + }, + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.roc new file mode 100644 index 00000000000..a7b93b838e4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.roc @@ -0,0 +1,5 @@ +data = + { x: 5, y: 0 } + |> &y 3 + +data diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.formatted.roc new file mode 100644 index 00000000000..f71b767eee8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.formatted.roc @@ -0,0 +1 @@ +foo &bar 5 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.result-ast new file mode 100644 index 00000000000..9ede64ccadb --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.result-ast @@ -0,0 +1,20 @@ +SpaceAfter( + Apply( + @0-3 Var { + module_name: "", + ident: "foo", + }, + [ + @3-7 RecordUpdater( + "bar", + ), + @9-10 Num( + "5", + ), + ], + Space, + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.roc new file mode 100644 index 00000000000..a56cffa35e9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_var_apply.expr.roc @@ -0,0 +1 @@ +foo&bar 5 diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 0ad5e2bb65f..a41dc389cd7 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -455,6 +455,8 @@ mod test_snapshots { pass/record_func_type_decl.expr, pass/record_type_with_function.expr, pass/record_update.expr, + pass/record_updater_literal_apply.expr, + pass/record_updater_var_apply.expr, pass/record_with_if.expr, pass/requires_type.header, pass/separate_defs.moduledefs, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 18514583258..d553da3db13 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -666,6 +666,7 @@ impl IterTokens for Loc> { Expr::SingleQuote(_) => onetoken(Token::String, region, arena), Expr::RecordAccess(rcd, _field) => Loc::at(region, *rcd).iter_tokens(arena), Expr::AccessorFunction(accessor) => Loc::at(region, accessor).iter_tokens(arena), + Expr::RecordUpdater(updater) => Loc::at(region, updater).iter_tokens(arena), Expr::TupleAccess(tup, _field) => Loc::at(region, *tup).iter_tokens(arena), Expr::TrySuffix { expr: inner, .. } => Loc::at(region, *inner).iter_tokens(arena), Expr::List(lst) => lst.iter_tokens(arena), diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 01d04a44be6..76e0964bf61 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1423,6 +1423,22 @@ fn to_bad_ident_expr_report<'b>( ]), ]), + StrayAmpersand(pos) => { + let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); + + alloc.stack([ + alloc.reflow(r"I am trying to parse a record updater function here:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), + alloc.concat([ + alloc.reflow("So I expect to see a lowercase letter next, like "), + alloc.parser_suggestion("&name"), + alloc.reflow(" or "), + alloc.parser_suggestion("&height"), + alloc.reflow("."), + ]), + ]) + } + WeirdDotQualified(pos) => { let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); @@ -1612,6 +1628,22 @@ fn to_bad_ident_pattern_report<'b>( ]) } + StrayAmpersand(pos) => { + let region = LineColumnRegion::from_pos(lines.convert_pos(pos)); + + alloc.stack([ + alloc.reflow(r"I am trying to parse a record updater function here:"), + alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), + alloc.concat([ + alloc.reflow("Something like "), + alloc.parser_suggestion("&name"), + alloc.reflow(" or "), + alloc.parser_suggestion("&height"), + alloc.reflow(" that updates a field in a record."), + ]), + ]) + } + WeirdAccessor(_pos) => alloc.stack([ alloc.reflow("I am very confused by this field access"), alloc.region(lines.convert_region(surroundings), severity), diff --git a/crates/reporting/src/report.rs b/crates/reporting/src/report.rs index 0e9e2d968d7..fcce0b44867 100644 --- a/crates/reporting/src/report.rs +++ b/crates/reporting/src/report.rs @@ -886,6 +886,7 @@ pub enum Annotation { Ellipsis, Tag, RecordField, + RecordUpdater, TupleElem, TypeVariable, Alias, @@ -1125,7 +1126,8 @@ where Warning => { self.write_str(self.palette.warning)?; } - TypeBlock | InlineTypeBlock | Tag | RecordField | TupleElem => { /* nothing yet */ } + TypeBlock | InlineTypeBlock | Tag | RecordField | RecordUpdater | TupleElem => { /* nothing yet */ + } } self.style_stack.push(*annotation); Ok(()) @@ -1144,8 +1146,8 @@ where self.write_str(self.palette.reset)?; } - TypeBlock | InlineTypeBlock | Tag | Opaque | RecordField | TupleElem => { /* nothing yet */ - } + TypeBlock | InlineTypeBlock | Tag | Opaque | RecordField | RecordUpdater + | TupleElem => { /* nothing yet */ } }, } Ok(()) From 209d78f72c6c56f007f4eb80bb7c389c50734077 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 01:52:56 -0700 Subject: [PATCH 184/203] Minor formatting fix --- crates/compiler/can/src/desugar.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 62f40c139db..b27356bb542 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -489,8 +489,9 @@ pub fn desugar_expr<'a>( }), fields: Collection::with_items( Vec::from_iter_in( - [ - Loc::at(region, AssignedField::RequiredValue( + [Loc::at( + region, + AssignedField::RequiredValue( Loc::at(region, field_name), &[], &*arena.alloc(Loc { @@ -500,12 +501,12 @@ pub fn desugar_expr<'a>( ident: "#record_updater_field", }, }), - )) - ], + ), + )], arena, ) .into_bump_slice(), - ) + ), }; arena.alloc(Loc { From e169529ede777adda944ba5f418b89f72c2d29ff Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 10:01:37 -0700 Subject: [PATCH 185/203] Remove barely used zstd dependency --- .../workflows/nightly_macos_apple_silicon.yml | 2 - Cargo.lock | 68 ------------------ Cargo.toml | 3 +- crates/compiler/module/Cargo.toml | 1 - crates/compiler/module/src/module_err.rs | 47 +++++++----- crates/compiler/module/src/symbol.rs | 12 ++-- crates/docs/Cargo.toml | 1 - crates/utils/error/Cargo.toml | 1 - crates/utils/error/src/lib.rs | 71 +++++++++++-------- www/content/install/macos_x86_64.md | 6 -- 10 files changed, 81 insertions(+), 131 deletions(-) diff --git a/.github/workflows/nightly_macos_apple_silicon.yml b/.github/workflows/nightly_macos_apple_silicon.yml index b4d12d700f4..5e9cc69ca73 100644 --- a/.github/workflows/nightly_macos_apple_silicon.yml +++ b/.github/workflows/nightly_macos_apple_silicon.yml @@ -14,8 +14,6 @@ jobs: test-and-build: name: Rust tests, build and package nightly release runs-on: [self-hosted, macOS, ARM64] - env: - LIBRARY_PATH: /opt/homebrew/Cellar/zstd/1.5.6/lib timeout-minutes: 90 steps: - uses: actions/checkout@v4 diff --git a/Cargo.lock b/Cargo.lock index 051e2aecb85..82726bc5616 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -723,17 +723,6 @@ dependencies = [ "powerfmt", ] -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "diff" version = "0.1.13" @@ -784,12 +773,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d9d8664cf849d7d0f3114a3a387d2f5e4303176d746d5a951aaddc66dfe9240" -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "dunce" version = "1.0.4" @@ -1700,11 +1683,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "crc32fast", - "flate2", "hashbrown", "indexmap", "memchr", - "ruzstd", ] [[package]] @@ -2516,7 +2497,6 @@ dependencies = [ "roc_solve", "roc_target", "roc_types", - "snafu", "ven_pretty", ] @@ -2536,9 +2516,6 @@ version = "0.0.1" [[package]] name = "roc_error_utils" version = "0.0.1" -dependencies = [ - "snafu", -] [[package]] name = "roc_exhaustive" @@ -2824,7 +2801,6 @@ dependencies = [ "roc_error_macros", "roc_ident", "roc_region", - "snafu", "static_assertions", ] @@ -3356,17 +3332,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "ruzstd" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58c4eb8a81997cf040a091d1f7e1938aeab6749d3a0dfa73af43cdc32393483d" -dependencies = [ - "byteorder", - "derive_more", - "twox-hash", -] - [[package]] name = "ryu" version = "1.0.15" @@ -3599,29 +3564,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" -[[package]] -name = "snafu" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" -dependencies = [ - "backtrace", - "doc-comment", - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "socket2" version = "0.4.9" @@ -4227,16 +4169,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if", - "static_assertions", -] - [[package]] name = "typed-arena" version = "2.0.2" diff --git a/Cargo.toml b/Cargo.toml index fd721ed6da0..a02dc7b7de2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,7 +137,7 @@ maplit = "1.0.2" memmap2 = "0.5.10" mimalloc = { version = "0.1.34", default-features = false } nonempty = "0.8.1" -object = { version = "0.32.2", features = ["read", "write"] } +object = { version = "0.32.2", default-features = false, features = ["read", "write"] } packed_struct = "0.10.1" page_size = "0.5.0" palette = "0.6.1" @@ -171,7 +171,6 @@ serde_json = "1.0.94" # update roc_std/Cargo.toml on change serial_test = "1.0.0" signal-hook = "0.3.15" smallvec = { version = "1.10.0", features = ["const_generics", "const_new"] } -snafu = { version = "0.7.4", features = ["backtraces"] } static_assertions = "1.1.0" # update roc_std/Cargo.toml on change strip-ansi-escapes = "0.1.1" strum = { version = "0.24.1", features = ["derive"] } diff --git a/crates/compiler/module/Cargo.toml b/crates/compiler/module/Cargo.toml index 6be6b4e716a..0474157dbba 100644 --- a/crates/compiler/module/Cargo.toml +++ b/crates/compiler/module/Cargo.toml @@ -14,7 +14,6 @@ roc_ident = { path = "../ident" } roc_region = { path = "../region" } bumpalo.workspace = true -snafu.workspace = true static_assertions.workspace = true [features] diff --git a/crates/compiler/module/src/module_err.rs b/crates/compiler/module/src/module_err.rs index f2b1f0b1680..497f222dc31 100644 --- a/crates/compiler/module/src/module_err.rs +++ b/crates/compiler/module/src/module_err.rs @@ -1,30 +1,45 @@ -use snafu::{Backtrace, Snafu}; - use crate::symbol::IdentId; -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] +#[derive(Debug)] pub enum ModuleError { - #[snafu(display( - "ModuleIdNotFound: I could not find the ModuleId {} in Interns.all_ident_ids: {}.", - module_id, - all_ident_ids - ))] ModuleIdNotFound { module_id: String, all_ident_ids: String, - backtrace: Backtrace, }, - #[snafu(display( - "IdentIdNotFound: I could not find IdentId {:?} in ident_ids {:?}.", - ident_id, - ident_ids_str - ))] IdentIdNotFound { ident_id: IdentId, ident_ids_str: String, - backtrace: Backtrace, }, } +impl std::fmt::Display for ModuleError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ModuleIdNotFound { + module_id, + all_ident_ids, + } => { + write!( + f, + "ModuleIdNotFound: I could not find the ModuleId {} in Interns.all_ident_ids: {}.", + module_id, + all_ident_ids + ) + } + Self::IdentIdNotFound { + ident_id, + ident_ids_str, + } => { + write!( + f, + "IdentIdNotFound: I could not find IdentId {:?} in ident_ids {:?}.", + ident_id, ident_ids_str + ) + } + } + } +} + +impl std::error::Error for ModuleError {} + pub type ModuleResult = std::result::Result; diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 385a98b5d05..ab2ddef015b 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1,10 +1,9 @@ use crate::ident::{Ident, Lowercase, ModuleName}; -use crate::module_err::{IdentIdNotFoundSnafu, ModuleIdNotFoundSnafu, ModuleResult}; +use crate::module_err::{ModuleError, ModuleResult}; use roc_collections::{SmallStringInterner, VecMap}; use roc_error_macros::internal_error; use roc_ident::IdentStr; use roc_region::all::Region; -use snafu::OptionExt; use std::num::NonZeroU32; use std::{fmt, u32}; @@ -324,7 +323,7 @@ pub fn get_module_ident_ids<'a>( ) -> ModuleResult<&'a IdentIds> { all_ident_ids .get(module_id) - .with_context(|| ModuleIdNotFoundSnafu { + .ok_or_else(|| ModuleError::ModuleIdNotFound { module_id: format!("{module_id:?}"), all_ident_ids: format!("{all_ident_ids:?}"), }) @@ -336,9 +335,10 @@ pub fn get_module_ident_ids_mut<'a>( ) -> ModuleResult<&'a mut IdentIds> { all_ident_ids .get_mut(module_id) - .with_context(|| ModuleIdNotFoundSnafu { + .ok_or_else(|| ModuleError::ModuleIdNotFound { module_id: format!("{module_id:?}"), - all_ident_ids: "I could not return all_ident_ids here because of borrowing issues.", + all_ident_ids: "I could not return all_ident_ids here because of borrowing issues." + .into(), }) } @@ -727,7 +727,7 @@ impl IdentIds { pub fn get_name_str_res(&self, ident_id: IdentId) -> ModuleResult<&str> { self.get_name(ident_id) - .with_context(|| IdentIdNotFoundSnafu { + .ok_or_else(|| ModuleError::IdentIdNotFound { ident_id, ident_ids_str: format!("{self:?}"), }) diff --git a/crates/docs/Cargo.toml b/crates/docs/Cargo.toml index e5337c5373a..3789c446791 100644 --- a/crates/docs/Cargo.toml +++ b/crates/docs/Cargo.toml @@ -27,4 +27,3 @@ ven_pretty = { path = "../vendor/pretty" } bumpalo.workspace = true pulldown-cmark.workspace = true -snafu.workspace = true diff --git a/crates/utils/error/Cargo.toml b/crates/utils/error/Cargo.toml index 85b4635dc86..6d037a6decc 100644 --- a/crates/utils/error/Cargo.toml +++ b/crates/utils/error/Cargo.toml @@ -8,6 +8,5 @@ license.workspace = true version.workspace = true [dependencies] -snafu.workspace = true [dev-dependencies] diff --git a/crates/utils/error/src/lib.rs b/crates/utils/error/src/lib.rs index d6b3584512e..156b2174aea 100644 --- a/crates/utils/error/src/lib.rs +++ b/crates/utils/error/src/lib.rs @@ -1,39 +1,55 @@ //! Provides utility functions used all over the code base. -use snafu::{Backtrace, OptionExt, Snafu}; use std::{collections::HashMap, slice::SliceIndex}; -#[derive(Debug, Snafu)] -#[snafu(visibility(pub))] +#[derive(Debug)] pub enum UtilError { - #[snafu(display( - "IndexOfFailed: Element {} was not found in collection {}.", - elt_str, - collection_str - ))] IndexOfFailed { elt_str: String, collection_str: String, - backtrace: Backtrace, }, - #[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))] KeyNotFound { key_str: String, - backtrace: Backtrace, }, - #[snafu(display( - "OutOfBounds: index {} was out of bounds for {} with length {}.", - index, - collection_name, - len - ))] OutOfBounds { index: usize, collection_name: String, len: usize, - backtrace: Backtrace, }, } +impl std::fmt::Display for UtilError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::IndexOfFailed { + elt_str, + collection_str, + } => { + write!( + f, + "IndexOfFailed: Element {} was not found in collection {}.", + elt_str, collection_str + ) + } + Self::KeyNotFound { key_str } => { + write!(f, "KeyNotFound: key {} was not found in HashMap.", key_str) + } + Self::OutOfBounds { + index, + collection_name, + len, + } => { + write!( + f, + "OutOfBounds: index {} was out of bounds for {} with length {}.", + index, collection_name, len + ) + } + } + } +} + +impl std::error::Error for UtilError {} + pub type UtilResult = std::result::Result; // replace HashMap method that returns Option with one that returns Result and proper Error @@ -41,7 +57,7 @@ pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( hash_map: &'a HashMap, key: &K, ) -> UtilResult<&'a V> { - let value = hash_map.get(key).context(KeyNotFoundSnafu { + let value = hash_map.get(key).ok_or_else(|| UtilError::KeyNotFound { key_str: format!("{key:?}"), })?; @@ -52,11 +68,11 @@ pub fn index_of(elt: T, slice: &[T]) -> Uti let index = slice .iter() .position(|slice_elt| *slice_elt == elt) - .with_context(|| { + .ok_or_else(|| { let elt_str = format!("{elt:?}"); let collection_str = format!("{slice:?}"); - IndexOfFailedSnafu { + UtilError::IndexOfFailed { elt_str, collection_str, } @@ -67,9 +83,9 @@ pub fn index_of(elt: T, slice: &[T]) -> Uti // replaces slice method that return Option with one that return Result and proper Error pub fn slice_get(index: usize, slice: &[T]) -> UtilResult<&>::Output> { - let elt_ref = slice.get(index).context(OutOfBoundsSnafu { + let elt_ref = slice.get(index).ok_or_else(|| UtilError::OutOfBounds { index, - collection_name: "Slice", + collection_name: "Slice".to_owned(), len: slice.len(), })?; @@ -82,9 +98,9 @@ pub fn slice_get_mut( ) -> UtilResult<&mut >::Output> { let slice_len = slice.len(); - let elt_ref = slice.get_mut(index).context(OutOfBoundsSnafu { + let elt_ref = slice.get_mut(index).ok_or_else(|| UtilError::OutOfBounds { index, - collection_name: "Slice", + collection_name: "Slice".to_owned(), len: slice_len, })?; @@ -118,10 +134,9 @@ pub fn first_last_index_of( let elt_str = format!("{elt:?}"); let collection_str = format!("{slice:?}"); - IndexOfFailedSnafu { + Err(UtilError::IndexOfFailed { elt_str, collection_str, - } - .fail() + }) } } diff --git a/www/content/install/macos_x86_64.md b/www/content/install/macos_x86_64.md index 819bdb1dcdc..9b7c19bf165 100644 --- a/www/content/install/macos_x86_64.md +++ b/www/content/install/macos_x86_64.md @@ -18,12 +18,6 @@ which includes the Roc compiler and some helpful utilities. cd roc_night ``` -1. Install required dependencies: - - ```sh - brew install z3 zstd - ``` - 1. To be able to run the `roc` command anywhere on your system; add the line below to your shell startup script (.profile, .zshrc, ...): ```sh From 7edb5a8e3b63a5c6ce5a0bbd8bf314b1a046e12b Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 10:31:56 -0700 Subject: [PATCH 186/203] Remove unused `backtrace` dependency --- Cargo.lock | 1 - crates/glue/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 82726bc5616..eb5c398edc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2606,7 +2606,6 @@ dependencies = [ name = "roc_glue" version = "0.0.1" dependencies = [ - "backtrace", "bumpalo", "cli_utils", "dircpy", diff --git a/crates/glue/Cargo.toml b/crates/glue/Cargo.toml index c01b7d94cb3..a93fc8fde07 100644 --- a/crates/glue/Cargo.toml +++ b/crates/glue/Cargo.toml @@ -25,7 +25,6 @@ roc_target = { path = "../compiler/roc_target" } roc_tracing = { path = "../tracing" } roc_types = { path = "../compiler/types" } -backtrace.workspace = true bumpalo.workspace = true fnv.workspace = true indexmap.workspace = true From 88b004fc8afcb5dee0577d06babcf530ce853149 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 10:35:53 -0700 Subject: [PATCH 187/203] Attempt to remove z3 dependency --- ci/basic_nightly_test.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ci/basic_nightly_test.sh b/ci/basic_nightly_test.sh index a23ef30ba20..be13a2ed465 100755 --- a/ci/basic_nightly_test.sh +++ b/ci/basic_nightly_test.sh @@ -17,10 +17,6 @@ if [ -n "$(ls | grep -v "roc_nightly.*tar\.gz" | grep -v "^ci$")" ]; then done fi -if [[ "$(uname)" == "Darwin" ]]; then - brew install z3 # used by llvm -fi - # decompress the tar ls | grep "roc_nightly.*tar\.gz" | xargs tar -xzvf From b0be69835403ae8db2e99fa583398fa2bc352598 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 17 Aug 2024 00:03:05 -0400 Subject: [PATCH 188/203] simplify `StrLiteral::Line` to `StrLiteral::PlainLine` when possible --- crates/compiler/parse/src/normalize.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 4f37a1e18ee..8aafe0f8307 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -624,6 +624,12 @@ impl<'a> Normalize<'a> for StrLiteral<'a> { new_segments.push(StrSegment::Plaintext(last_text.into_bump_str())); } + if new_segments.len() == 1 { + if let StrSegment::Plaintext(t) = new_segments[0] { + return StrLiteral::PlainLine(t); + } + } + StrLiteral::Line(new_segments.into_bump_slice()) } StrLiteral::Block(t) => { From 7be537dd552a315f71f9b1a56b941851bce1df51 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sun, 4 Aug 2024 13:00:11 -0400 Subject: [PATCH 189/203] format invisible characters in strings to unicode escapes --- crates/compiler/fmt/src/expr.rs | 24 ++++++++-- crates/compiler/test_syntax/tests/test_fmt.rs | 46 +++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 645b258a617..f937a2c7e3d 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -618,6 +618,22 @@ fn starts_with_newline(expr: &Expr) -> bool { } } +fn fmt_str_body(body: &str, buf: &mut Buf) { + for c in body.chars() { + match c { + // Format blank characters as unicode escapes + '\u{200a}' => buf.push_str("\\u(200a)"), + '\u{200b}' => buf.push_str("\\u(200b)"), + '\u{200c}' => buf.push_str("\\u(200c)"), + '\u{feff}' => buf.push_str("\\u(feff)"), + // Don't change anything else in the string + ' ' => buf.spaces(1), + '\n' => buf.newline(), + _ => buf.push(c), + } + } +} + fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) { use StrSegment::*; @@ -627,10 +643,10 @@ fn format_str_segment(seg: &StrSegment, buf: &mut Buf, indent: u16) { // a line break in the input string match string.strip_suffix('\n') { Some(string_without_newline) => { - buf.push_str_allow_spaces(string_without_newline); + fmt_str_body(string_without_newline, buf); buf.newline(); } - None => buf.push_str_allow_spaces(string), + None => fmt_str_body(string, buf), } } Unicode(loc_str) => { @@ -690,7 +706,7 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) { buf.push_newline_literal(); for line in string.split('\n') { buf.indent(indent); - buf.push_str_allow_spaces(line); + fmt_str_body(line, buf); buf.push_newline_literal(); } buf.indent(indent); @@ -698,7 +714,7 @@ pub fn fmt_str_literal(buf: &mut Buf, literal: StrLiteral, indent: u16) { } else { buf.indent(indent); buf.push('"'); - buf.push_str_allow_spaces(string); + fmt_str_body(string, buf); buf.push('"'); }; } diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 026605d825c..519bcbd5513 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6333,6 +6333,52 @@ mod test_fmt { ); } + #[test] + fn keep_explicit_blank_chars() { + expr_formats_same(indoc!( + r#" + x = "a\u(200a)b\u(200b)c\u(200c)d\u(feff)e" + x + "# + )); + } + + #[test] + fn make_blank_chars_explicit() { + expr_formats_to( + indoc!( + " + x = \"a\u{200A}b\u{200B}c\u{200C}d\u{FEFF}e\" + x + " + ), + indoc!( + r#" + x = "a\u(200a)b\u(200b)c\u(200c)d\u(feff)e" + x + "# + ), + ); + } + + #[test] + fn make_blank_chars_explicit_when_interpolating() { + expr_formats_to( + indoc!( + " + x = \"foo:\u{200B} $(bar).\" + x + " + ), + indoc!( + r#" + x = "foo:\u(200b) $(bar)." + x + "# + ), + ); + } + // this is a parse error atm // #[test] // fn multiline_apply() { From 50f6e1142373c8004234a2b99206ec4c4f956f55 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 22:36:04 -0700 Subject: [PATCH 190/203] Deprecate backpassing to prepare for eventual removal --- crates/cli/tests/benchmarks/Base64/Decode.roc | 51 ++- crates/cli/tests/benchmarks/cFold.roc | 9 +- crates/cli/tests/benchmarks/deriv.roc | 20 +- crates/cli/tests/benchmarks/nQueens.roc | 9 +- crates/cli/tests/benchmarks/platform/Task.roc | 35 +- crates/cli/tests/benchmarks/quicksortApp.roc | 10 +- crates/cli/tests/benchmarks/rBTreeCk.roc | 9 +- crates/cli/tests/benchmarks/rBTreeDel.roc | 11 +- crates/cli/tests/cli/countdown.roc | 12 +- crates/cli/tests/cli/echo.roc | 2 +- crates/cli/tests/cli/parse-args.roc | 22 +- crates/cli/tests/cli/parser-letter-counts.roc | 23 +- crates/compiler/builtins/roc/Dict.roc | 24 +- crates/compiler/builtins/roc/Inspect.roc | 254 +++++++------- crates/compiler/builtins/roc/Set.roc | 4 +- crates/compiler/can/src/desugar.rs | 310 ++++++++++++++---- crates/compiler/can/src/module.rs | 10 +- crates/compiler/can/tests/helpers/mod.rs | 1 + crates/compiler/can/tests/test_suffixed.rs | 10 +- crates/compiler/load/tests/helpers/mod.rs | 1 + crates/compiler/load/tests/test_reporting.rs | 83 ++--- crates/compiler/module/src/called_via.rs | 17 + crates/compiler/problem/src/can.rs | 3 + crates/compiler/test_mono/src/tests.rs | 4 +- crates/reporting/src/error/canonicalize.rs | 23 +- crates/reporting/src/error/type.rs | 4 +- crates/reporting/src/report.rs | 11 + examples/Community.roc | 39 +-- examples/cli/false-interpreter/Context.roc | 14 +- examples/cli/false-interpreter/False.roc | 86 ++--- .../cli/false-interpreter/platform/File.roc | 7 +- .../cli/false-interpreter/platform/Task.roc | 18 +- .../platform/Html/Internal/Client.roc | 33 +- examples/virtual-dom-wip/platform/Json.roc | 129 ++++---- 34 files changed, 793 insertions(+), 505 deletions(-) diff --git a/crates/cli/tests/benchmarks/Base64/Decode.roc b/crates/cli/tests/benchmarks/Base64/Decode.roc index ebc2068e418..4b32bff95af 100644 --- a/crates/cli/tests/benchmarks/Base64/Decode.roc +++ b/crates/cli/tests/benchmarks/Base64/Decode.roc @@ -1,4 +1,4 @@ -interface Base64.Decode exposes [fromBytes] imports [] +module [fromBytes] import Bytes.Decode exposing [ByteDecoder, DecodeProblem] @@ -12,40 +12,39 @@ decodeBase64 = \width -> Bytes.Decode.loop loopHelp { remaining: width, string: loopHelp : { remaining : U64, string : Str } -> ByteDecoder (Bytes.Decode.Step { remaining : U64, string : Str } Str) loopHelp = \{ remaining, string } -> if remaining >= 3 then - x, y, z <- Bytes.Decode.map3 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.u8 - - a : U32 - a = Num.intCast x - b : U32 - b = Num.intCast y - c : U32 - c = Num.intCast z - combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)) c - - Loop { - remaining: remaining - 3, - string: Str.concat string (bitsToChars combined 0), - } + Bytes.Decode.map3 Bytes.Decode.u8 Bytes.Decode.u8 Bytes.Decode.u8 \x, y, z -> + a : U32 + a = Num.intCast x + b : U32 + b = Num.intCast y + c : U32 + c = Num.intCast z + combined = Num.bitwiseOr (Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8)) c + + Loop { + remaining: remaining - 3, + string: Str.concat string (bitsToChars combined 0), + } else if remaining == 0 then Bytes.Decode.succeed (Done string) else if remaining == 2 then - x, y <- Bytes.Decode.map2 Bytes.Decode.u8 Bytes.Decode.u8 + Bytes.Decode.map2 Bytes.Decode.u8 Bytes.Decode.u8 \x, y -> - a : U32 - a = Num.intCast x - b : U32 - b = Num.intCast y - combined = Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8) + a : U32 + a = Num.intCast x + b : U32 + b = Num.intCast y + combined = Num.bitwiseOr (Num.shiftLeftBy a 16) (Num.shiftLeftBy b 8) - Done (Str.concat string (bitsToChars combined 1)) + Done (Str.concat string (bitsToChars combined 1)) else # remaining = 1 - x <- Bytes.Decode.map Bytes.Decode.u8 + Bytes.Decode.map Bytes.Decode.u8 \x -> - a : U32 - a = Num.intCast x + a : U32 + a = Num.intCast x - Done (Str.concat string (bitsToChars (Num.shiftLeftBy a 16) 2)) + Done (Str.concat string (bitsToChars (Num.shiftLeftBy a 16) 2)) bitsToChars : U32, Int * -> Str bitsToChars = \bits, missing -> diff --git a/crates/cli/tests/benchmarks/cFold.roc b/crates/cli/tests/benchmarks/cFold.roc index e4b1e2423dd..d9c5791ece5 100644 --- a/crates/cli/tests/benchmarks/cFold.roc +++ b/crates/cli/tests/benchmarks/cFold.roc @@ -1,12 +1,11 @@ -app "cfold" - packages { pf: "platform/main.roc" } - imports [pf.Task] - provides [main] to pf +app [main] { pf: platform "platform/main.roc" } + +import pf.Task # adapted from https://github.com/koka-lang/koka/blob/master/test/bench/haskell/cfold.hs main : Task.Task {} [] main = - inputResult <- Task.attempt Task.getInt + inputResult = Task.getInt |> Task.result! when inputResult is Ok n -> diff --git a/crates/cli/tests/benchmarks/deriv.roc b/crates/cli/tests/benchmarks/deriv.roc index 7e4d22a6c29..2add5926700 100644 --- a/crates/cli/tests/benchmarks/deriv.roc +++ b/crates/cli/tests/benchmarks/deriv.roc @@ -1,14 +1,13 @@ -app "deriv" - packages { pf: "platform/main.roc" } - imports [pf.Task] - provides [main] to pf +app [main] { pf: platform "platform/main.roc" } + +import pf.Task # based on: https://github.com/koka-lang/koka/blob/master/test/bench/haskell/deriv.hs IO a : Task.Task a [] main : Task.Task {} [] main = - inputResult <- Task.attempt Task.getInt + inputResult = Task.getInt |> Task.result! when inputResult is Ok n -> @@ -25,11 +24,12 @@ main = Task.putLine "Error: Failed to get Integer from stdin." nestHelp : I64, (I64, Expr -> IO Expr), I64, Expr -> IO Expr -nestHelp = \s, f, m, x -> when m is - 0 -> Task.succeed x - _ -> - w <- Task.after (f (s - m) x) - nestHelp s f (m - 1) w +nestHelp = \s, f, m, x -> + when m is + 0 -> Task.succeed x + _ -> + Task.after (f (s - m) x) \w -> + nestHelp s f (m - 1) w nest : (I64, Expr -> IO Expr), I64, Expr -> IO Expr nest = \f, n, e -> nestHelp n f n e diff --git a/crates/cli/tests/benchmarks/nQueens.roc b/crates/cli/tests/benchmarks/nQueens.roc index 15593e37216..82e331ef479 100644 --- a/crates/cli/tests/benchmarks/nQueens.roc +++ b/crates/cli/tests/benchmarks/nQueens.roc @@ -1,11 +1,10 @@ -app "nqueens" - packages { pf: "platform/main.roc" } - imports [pf.Task] - provides [main] to pf +app [main] { pf: platform "platform/main.roc" } + +import pf.Task main : Task.Task {} [] main = - inputResult <- Task.attempt Task.getInt + inputResult = Task.getInt |> Task.result! when inputResult is Ok n -> diff --git a/crates/cli/tests/benchmarks/platform/Task.roc b/crates/cli/tests/benchmarks/platform/Task.roc index 16da2d1eb73..d948e7d7987 100644 --- a/crates/cli/tests/benchmarks/platform/Task.roc +++ b/crates/cli/tests/benchmarks/platform/Task.roc @@ -1,6 +1,20 @@ -interface Task - exposes [Task, succeed, fail, after, map, putLine, putInt, getInt, forever, loop, attempt] - imports [pf.Effect] +module [ + Task, + await, + succeed, + fail, + after, + map, + result, + putLine, + putInt, + getInt, + forever, + loop, + attempt, +] + +import pf.Effect Task ok err : Effect.Effect (Result ok err) @@ -46,6 +60,15 @@ after = \effect, transform -> Ok a -> transform a Err err -> Task.fail err +await : Task a err, (a -> Task b err) -> Task b err +await = \effect, transform -> + Effect.after + effect + \result -> + when result is + Ok a -> transform a + Err err -> Task.fail err + attempt : Task a b, (Result a b -> Task c d) -> Task c d attempt = \task, transform -> Effect.after @@ -64,6 +87,12 @@ map = \effect, transform -> Ok a -> Ok (transform a) Err err -> Err err +result : Task ok err -> Task (Result ok err) * +result = \effect -> + Effect.after + effect + \result -> Task.succeed result + putLine : Str -> Task {} * putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) diff --git a/crates/cli/tests/benchmarks/quicksortApp.roc b/crates/cli/tests/benchmarks/quicksortApp.roc index 67766bc9820..66b39ab2bf5 100644 --- a/crates/cli/tests/benchmarks/quicksortApp.roc +++ b/crates/cli/tests/benchmarks/quicksortApp.roc @@ -1,11 +1,11 @@ -app "quicksortapp" - packages { pf: "platform/main.roc" } - imports [pf.Task, Quicksort] - provides [main] to pf +app [main] { pf: platform "platform/main.roc" } + +import pf.Task +import Quicksort main : Task.Task {} [] main = - inputResult <- Task.attempt Task.getInt + inputResult = Task.getInt |> Task.result! when inputResult is Ok n -> diff --git a/crates/cli/tests/benchmarks/rBTreeCk.roc b/crates/cli/tests/benchmarks/rBTreeCk.roc index 5c685b195e6..0d7f1c52325 100644 --- a/crates/cli/tests/benchmarks/rBTreeCk.roc +++ b/crates/cli/tests/benchmarks/rBTreeCk.roc @@ -1,7 +1,6 @@ -app "rbtree-ck" - packages { pf: "platform/main.roc" } - imports [pf.Task] - provides [main] to pf +app [main] { pf: platform "platform/main.roc" } + +import pf.Task Color : [Red, Black] @@ -40,7 +39,7 @@ fold = \f, tree, b -> main : Task.Task {} [] main = - inputResult <- Task.attempt Task.getInt + inputResult = Task.getInt |> Task.result! when inputResult is Ok n -> diff --git a/crates/cli/tests/benchmarks/rBTreeDel.roc b/crates/cli/tests/benchmarks/rBTreeDel.roc index 5617d6eb3f6..82b333c4883 100644 --- a/crates/cli/tests/benchmarks/rBTreeDel.roc +++ b/crates/cli/tests/benchmarks/rBTreeDel.roc @@ -1,7 +1,6 @@ -app "rbtree-del" - packages { pf: "platform/main.roc" } - imports [pf.Task] - provides [main] to pf +app [main] { pf: platform "platform/main.roc" } + +import pf.Task Color : [Red, Black] @@ -13,7 +12,7 @@ ConsList a : [Nil, Cons a (ConsList a)] main : Task.Task {} [] main = - inputResult <- Task.attempt Task.getInt + inputResult = Task.getInt |> Task.result! when inputResult is Ok n -> @@ -248,4 +247,4 @@ del = \t, k -> rebalanceLeft cx lx ky vy ry Delmin (Del ry Bool.false) ky vy -> - Del (Node cx lx ky vy ry) Bool.false \ No newline at end of file + Del (Node cx lx ky vy ry) Bool.false diff --git a/crates/cli/tests/cli/countdown.roc b/crates/cli/tests/cli/countdown.roc index bea034af1ea..26b1ce386a2 100644 --- a/crates/cli/tests/cli/countdown.roc +++ b/crates/cli/tests/cli/countdown.roc @@ -2,18 +2,18 @@ app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/downlo import pf.Stdin import pf.Stdout -import pf.Task exposing [await, loop] +import pf.Task exposing [Task, loop] main = - _ <- await (Stdout.line "\nLet's count down from 3 together - all you have to do is press .") - _ <- await Stdin.line + Stdout.line! "\nLet's count down from 3 together - all you have to do is press ." + _ = Stdin.line! loop 3 tick tick = \n -> if n == 0 then - _ <- await (Stdout.line "🎉 SURPRISE! Happy Birthday! 🎂") + Stdout.line! "🎉 SURPRISE! Happy Birthday! 🎂" Task.ok (Done {}) else - _ <- await (n |> Num.toStr |> \s -> "$(s)..." |> Stdout.line) - _ <- await Stdin.line + Stdout.line! (n |> Num.toStr |> \s -> "$(s)...") + _ = Stdin.line! Task.ok (Step (n - 1)) diff --git a/crates/cli/tests/cli/echo.roc b/crates/cli/tests/cli/echo.roc index dd6572a957f..5c77c7514df 100644 --- a/crates/cli/tests/cli/echo.roc +++ b/crates/cli/tests/cli/echo.roc @@ -5,7 +5,7 @@ import pf.Stdout import pf.Task exposing [Task] main = - _ <- Task.await (Stdout.line "🗣 Shout into this cave and hear the echo! 👂👂👂") + Stdout.line! "🗣 Shout into this cave and hear the echo! 👂👂👂" Task.loop {} tick diff --git a/crates/cli/tests/cli/parse-args.roc b/crates/cli/tests/cli/parse-args.roc index 145a2e36e47..983d297ca55 100644 --- a/crates/cli/tests/cli/parse-args.roc +++ b/crates/cli/tests/cli/parse-args.roc @@ -55,23 +55,23 @@ numParam = \{ name } -> { params: [param], parser } cliMap : ArgParser a, (a -> b) -> ArgParser b -cliMap = \{ params, parser }, mapper -> { - params, - parser: \args -> - (data, afterData) <- parser args - |> Result.try +cliMap = \{ params, parser }, mapper -> + mappedParser = \args -> + (data, afterData) = parser? args - Ok (mapper data, afterData), -} + Ok (mapper data, afterData) + + { + params, + parser: mappedParser, + } cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c cliBuild = \firstWeaver, secondWeaver, combine -> allParams = List.concat firstWeaver.params secondWeaver.params combinedParser = \args -> - (firstValue, afterFirst) <- firstWeaver.parser args - |> Result.try - (secondValue, afterSecond) <- secondWeaver.parser afterFirst - |> Result.try + (firstValue, afterFirst) = firstWeaver.parser? args + (secondValue, afterSecond) = secondWeaver.parser? afterFirst Ok (combine firstValue secondValue, afterSecond) diff --git a/crates/cli/tests/cli/parser-letter-counts.roc b/crates/cli/tests/cli/parser-letter-counts.roc index ba9780e94d5..4e1a749cf8c 100644 --- a/crates/cli/tests/cli/parser-letter-counts.roc +++ b/crates/cli/tests/cli/parser-letter-counts.roc @@ -26,18 +26,17 @@ Letter : [A, B, C, Other] letterParser : Parser (List U8) Letter letterParser = - input <- buildPrimitiveParser - - valResult = - when input is - [] -> Err (ParsingFailure "Nothing to parse") - ['A', ..] -> Ok A - ['B', ..] -> Ok B - ['C', ..] -> Ok C - _ -> Ok Other - - valResult - |> Result.map \val -> { val, input: List.dropFirst input 1 } + buildPrimitiveParser \input -> + valResult = + when input is + [] -> Err (ParsingFailure "Nothing to parse") + ['A', ..] -> Ok A + ['B', ..] -> Ok B + ['C', ..] -> Ok C + _ -> Ok Other + + valResult + |> Result.map \val -> { val, input: List.dropFirst input 1 } expect input = "B" diff --git a/crates/compiler/builtins/roc/Dict.roc b/crates/compiler/builtins/roc/Dict.roc index f6d17e28796..31c95b77643 100644 --- a/crates/compiler/builtins/roc/Dict.roc +++ b/crates/compiler/builtins/roc/Dict.roc @@ -129,8 +129,8 @@ hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk toInspectorDict : Dict k v -> Inspector f where k implements Inspect & Hash & Eq, v implements Inspect, f implements InspectFormatter toInspectorDict = \dict -> - fmt <- Inspect.custom - Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt + Inspect.custom \fmt -> + Inspect.apply (Inspect.dict dict walk Inspect.toInspector Inspect.toInspector) fmt ## Return an empty dictionary. ## ```roc @@ -894,9 +894,9 @@ calcNumBuckets = \shifts -> maxBucketCount fillBucketsFromData = \buckets0, data, shifts -> - buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0 - (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts - placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex + List.walkWithIndex data buckets0 \buckets1, (key, _), dataIndex -> + (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts + placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex nextWhileLess : List Bucket, k, U8 -> (U64, U32) where k implements Hash & Eq nextWhileLess = \buckets, key, shifts -> @@ -1213,15 +1213,15 @@ expect ] dict = - acc, k <- List.walk badKeys (Dict.empty {}) - Dict.update acc k \val -> - when val is - Present p -> Present (p |> Num.addWrap 1) - Missing -> Present 0 + List.walk badKeys (Dict.empty {}) \acc, k -> + Dict.update acc k \val -> + when val is + Present p -> Present (p |> Num.addWrap 1) + Missing -> Present 0 allInsertedCorrectly = - acc, k <- List.walk badKeys Bool.true - acc && Dict.contains dict k + List.walk badKeys Bool.true \acc, k -> + acc && Dict.contains dict k allInsertedCorrectly diff --git a/crates/compiler/builtins/roc/Inspect.roc b/crates/compiler/builtins/roc/Inspect.roc index 0f9c08a213f..ed7db5369e7 100644 --- a/crates/compiler/builtins/roc/Inspect.roc +++ b/crates/compiler/builtins/roc/Inspect.roc @@ -138,203 +138,203 @@ dbgInit = \{} -> @DbgFormatter { data: "" } dbgList : list, ElemWalker (DbgFormatter, Bool) list elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter dbgList = \content, walkFn, toDbgInspector -> - f0 <- custom - dbgWrite f0 "[" - |> \f1 -> - (f2, prependSep), elem <- walkFn content (f1, Bool.false) - f3 = - if prependSep then - dbgWrite f2 ", " - else - f2 - - elem - |> toDbgInspector - |> apply f3 - |> \f4 -> (f4, Bool.true) - |> .0 - |> dbgWrite "]" + custom \f0 -> + dbgWrite f0 "[" + |> \f1 -> + walkFn content (f1, Bool.false) \(f2, prependSep), elem -> + f3 = + if prependSep then + dbgWrite f2 ", " + else + f2 + + elem + |> toDbgInspector + |> apply f3 + |> \f4 -> (f4, Bool.true) + |> .0 + |> dbgWrite "]" dbgSet : set, ElemWalker (DbgFormatter, Bool) set elem, (elem -> Inspector DbgFormatter) -> Inspector DbgFormatter dbgSet = \content, walkFn, toDbgInspector -> - f0 <- custom - dbgWrite f0 "{" - |> \f1 -> - (f2, prependSep), elem <- walkFn content (f1, Bool.false) - f3 = - if prependSep then - dbgWrite f2 ", " - else - f2 - - elem - |> toDbgInspector - |> apply f3 - |> \f4 -> (f4, Bool.true) - |> .0 - |> dbgWrite "}" + custom \f0 -> + dbgWrite f0 "{" + |> \f1 -> + walkFn content (f1, Bool.false) \(f2, prependSep), elem -> + f3 = + if prependSep then + dbgWrite f2 ", " + else + f2 + + elem + |> toDbgInspector + |> apply f3 + |> \f4 -> (f4, Bool.true) + |> .0 + |> dbgWrite "}" dbgDict : dict, KeyValWalker (DbgFormatter, Bool) dict key value, (key -> Inspector DbgFormatter), (value -> Inspector DbgFormatter) -> Inspector DbgFormatter dbgDict = \d, walkFn, keyToInspector, valueToInspector -> - f0 <- custom - dbgWrite f0 "{" - |> \f1 -> - (f2, prependSep), key, value <- walkFn d (f1, Bool.false) - f3 = - if prependSep then - dbgWrite f2 ", " - else - f2 - - apply (keyToInspector key) f3 - |> dbgWrite ": " - |> \x -> apply (valueToInspector value) x - |> \f4 -> (f4, Bool.true) - |> .0 - |> dbgWrite "}" + custom \f0 -> + dbgWrite f0 "{" + |> \f1 -> + walkFn d (f1, Bool.false) \(f2, prependSep), key, value -> + f3 = + if prependSep then + dbgWrite f2 ", " + else + f2 + + apply (keyToInspector key) f3 + |> dbgWrite ": " + |> \x -> apply (valueToInspector value) x + |> \f4 -> (f4, Bool.true) + |> .0 + |> dbgWrite "}" dbgTag : Str, List (Inspector DbgFormatter) -> Inspector DbgFormatter dbgTag = \name, fields -> if List.isEmpty fields then - f0 <- custom - dbgWrite f0 name + custom \f0 -> + dbgWrite f0 name else - f0 <- custom - dbgWrite f0 "(" - |> dbgWrite name - |> \f1 -> - f2, inspector <- List.walk fields f1 - dbgWrite f2 " " - |> \x -> apply inspector x - |> dbgWrite ")" + custom \f0 -> + dbgWrite f0 "(" + |> dbgWrite name + |> \f1 -> + List.walk fields f1 \f2, inspector -> + dbgWrite f2 " " + |> \x -> apply inspector x + |> dbgWrite ")" dbgTuple : List (Inspector DbgFormatter) -> Inspector DbgFormatter dbgTuple = \fields -> - f0 <- custom - dbgWrite f0 "(" - |> \f1 -> - (f2, prependSep), inspector <- List.walk fields (f1, Bool.false) - f3 = - if prependSep then - dbgWrite f2 ", " - else - f2 - - apply inspector f3 - |> \f4 -> (f4, Bool.true) - |> .0 - |> dbgWrite ")" + custom \f0 -> + dbgWrite f0 "(" + |> \f1 -> + List.walk fields (f1, Bool.false) \(f2, prependSep), inspector -> + f3 = + if prependSep then + dbgWrite f2 ", " + else + f2 + + apply inspector f3 + |> \f4 -> (f4, Bool.true) + |> .0 + |> dbgWrite ")" dbgRecord : List { key : Str, value : Inspector DbgFormatter } -> Inspector DbgFormatter dbgRecord = \fields -> - f0 <- custom - dbgWrite f0 "{" - |> \f1 -> - (f2, prependSep), { key, value } <- List.walk fields (f1, Bool.false) - f3 = - if prependSep then - dbgWrite f2 ", " - else - f2 - - dbgWrite f3 key - |> dbgWrite ": " - |> \x -> apply value x - |> \f4 -> (f4, Bool.true) - |> .0 - |> dbgWrite "}" + custom \f0 -> + dbgWrite f0 "{" + |> \f1 -> + List.walk fields (f1, Bool.false) \(f2, prependSep), { key, value } -> + f3 = + if prependSep then + dbgWrite f2 ", " + else + f2 + + dbgWrite f3 key + |> dbgWrite ": " + |> \x -> apply value x + |> \f4 -> (f4, Bool.true) + |> .0 + |> dbgWrite "}" dbgBool : Bool -> Inspector DbgFormatter dbgBool = \b -> if b then - f0 <- custom - dbgWrite f0 "Bool.true" + custom \f0 -> + dbgWrite f0 "Bool.true" else - f0 <- custom - dbgWrite f0 "Bool.false" + custom \f0 -> + dbgWrite f0 "Bool.false" dbgStr : Str -> Inspector DbgFormatter dbgStr = \s -> - f0 <- custom - f0 - |> dbgWrite "\"" - |> dbgWrite s # TODO: Should we be escaping strings for dbg/logging? - |> dbgWrite "\"" + custom \f0 -> + f0 + |> dbgWrite "\"" + |> dbgWrite s # TODO: Should we be escaping strings for dbg/logging? + |> dbgWrite "\"" dbgOpaque : * -> Inspector DbgFormatter dbgOpaque = \_ -> - f0 <- custom - dbgWrite f0 "" + custom \f0 -> + dbgWrite f0 "" dbgFunction : * -> Inspector DbgFormatter dbgFunction = \_ -> - f0 <- custom - dbgWrite f0 "" + custom \f0 -> + dbgWrite f0 "" dbgU8 : U8 -> Inspector DbgFormatter dbgU8 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgI8 : I8 -> Inspector DbgFormatter dbgI8 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgU16 : U16 -> Inspector DbgFormatter dbgU16 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgI16 : I16 -> Inspector DbgFormatter dbgI16 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgU32 : U32 -> Inspector DbgFormatter dbgU32 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgI32 : I32 -> Inspector DbgFormatter dbgI32 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgU64 : U64 -> Inspector DbgFormatter dbgU64 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgI64 : I64 -> Inspector DbgFormatter dbgI64 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgU128 : U128 -> Inspector DbgFormatter dbgU128 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgI128 : I128 -> Inspector DbgFormatter dbgI128 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgF32 : F32 -> Inspector DbgFormatter dbgF32 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgF64 : F64 -> Inspector DbgFormatter dbgF64 = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgDec : Dec -> Inspector DbgFormatter dbgDec = \num -> - f0 <- custom - dbgWrite f0 (num |> Num.toStr) + custom \f0 -> + dbgWrite f0 (num |> Num.toStr) dbgWrite : DbgFormatter, Str -> DbgFormatter dbgWrite = \@DbgFormatter { data }, added -> diff --git a/crates/compiler/builtins/roc/Set.roc b/crates/compiler/builtins/roc/Set.roc index c68ae1f2fdf..4d0c4a7332d 100644 --- a/crates/compiler/builtins/roc/Set.roc +++ b/crates/compiler/builtins/roc/Set.roc @@ -62,8 +62,8 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter toInspectorSet = \set -> - fmt <- Inspect.custom - Inspect.apply (Inspect.set set walk Inspect.toInspector) fmt + Inspect.custom \fmt -> + Inspect.apply (Inspect.set set walk Inspect.toInspector) fmt ## Creates a new empty `Set`. ## ```roc diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 75cc844a773..13a00cc0435 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -12,6 +12,7 @@ use roc_parse::ast::{ AssignedField, Collection, ModuleImportParams, OldRecordBuilderField, Pattern, StrLiteral, StrSegment, TypeAnnotation, ValueDef, WhenBranch, }; +use roc_problem::can::Problem; use roc_region::all::{LineInfo, Loc, Region}; // BinOp precedence logic adapted from Gluon by Markus Westerlind @@ -74,13 +75,14 @@ fn desugar_value_def<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> ValueDef<'a> { use ValueDef::*; match def { Body(loc_pattern, loc_expr) => Body( - desugar_loc_pattern(arena, loc_pattern, src, line_info, module_path), - desugar_expr(arena, loc_expr, src, line_info, module_path), + desugar_loc_pattern(arena, loc_pattern, src, line_info, module_path, problems), + desugar_expr(arena, loc_expr, src, line_info, module_path, problems), ), ann @ Annotation(_, _) => *ann, AnnotatedBody { @@ -93,16 +95,29 @@ fn desugar_value_def<'a>( ann_pattern, ann_type, lines_between, - body_pattern: desugar_loc_pattern(arena, body_pattern, src, line_info, module_path), - body_expr: desugar_expr(arena, body_expr, src, line_info, module_path), + body_pattern: desugar_loc_pattern( + arena, + body_pattern, + src, + line_info, + module_path, + problems, + ), + body_expr: desugar_expr(arena, body_expr, src, line_info, module_path, problems), }, Dbg { condition, preceding_comment, } => { - let desugared_condition = - &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); + let desugared_condition = &*arena.alloc(desugar_expr( + arena, + condition, + src, + line_info, + module_path, + problems, + )); Dbg { condition: desugared_condition, preceding_comment: *preceding_comment, @@ -112,8 +127,14 @@ fn desugar_value_def<'a>( condition, preceding_comment, } => { - let desugared_condition = - &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); + let desugared_condition = &*arena.alloc(desugar_expr( + arena, + condition, + src, + line_info, + module_path, + problems, + )); Expect { condition: desugared_condition, preceding_comment: *preceding_comment, @@ -123,8 +144,14 @@ fn desugar_value_def<'a>( condition, preceding_comment, } => { - let desugared_condition = - &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); + let desugared_condition = &*arena.alloc(desugar_expr( + arena, + condition, + src, + line_info, + module_path, + problems, + )); ExpectFx { condition: desugared_condition, preceding_comment: *preceding_comment, @@ -141,7 +168,14 @@ fn desugar_value_def<'a>( params.map(|ModuleImportParams { before, params }| ModuleImportParams { before, params: params.map(|params| { - desugar_field_collection(arena, *params, src, line_info, module_path) + desugar_field_collection( + arena, + *params, + src, + line_info, + module_path, + problems, + ) }), }); @@ -174,7 +208,7 @@ fn desugar_value_def<'a>( )), lines_between: &[], body_pattern: new_pat, - body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path), + body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path, problems), } } } @@ -187,9 +221,17 @@ pub fn desugar_defs_node_values<'a>( line_info: &mut Option, module_path: &str, top_level_def: bool, + problems: &mut std::vec::Vec, ) { for value_def in defs.value_defs.iter_mut() { - *value_def = desugar_value_def(arena, arena.alloc(*value_def), src, line_info, module_path); + *value_def = desugar_value_def( + arena, + arena.alloc(*value_def), + src, + line_info, + module_path, + problems, + ); } // `desugar_defs_node_values` is called recursively in `desugar_expr` @@ -312,6 +354,7 @@ pub fn desugar_expr<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> &'a Loc> { match &loc_expr.value { Float(..) @@ -344,6 +387,7 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, ))); arena.alloc(Loc { region, value }) @@ -352,7 +396,7 @@ pub fn desugar_expr<'a>( let region = loc_expr.region; let new_lines = Vec::from_iter_in( lines.iter().map(|segments| { - desugar_str_segments(arena, segments, src, line_info, module_path) + desugar_str_segments(arena, segments, src, line_info, module_path, problems) }), arena, ); @@ -375,6 +419,7 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, ) .value, paths, @@ -389,7 +434,8 @@ pub fn desugar_expr<'a>( target, } => { let intermediate = arena.alloc(Loc::at(loc_expr.region, **sub_expr)); - let new_sub_loc_expr = desugar_expr(arena, intermediate, src, line_info, module_path); + let new_sub_loc_expr = + desugar_expr(arena, intermediate, src, line_info, module_path, problems); let new_sub_expr = arena.alloc(new_sub_loc_expr.value); arena.alloc(Loc::at( @@ -413,6 +459,7 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, ) .value, paths, @@ -424,7 +471,14 @@ pub fn desugar_expr<'a>( let mut new_items = Vec::with_capacity_in(items.len(), arena); for item in items.iter() { - new_items.push(desugar_expr(arena, item, src, line_info, module_path)); + new_items.push(desugar_expr( + arena, + item, + src, + line_info, + module_path, + problems, + )); } let new_items = new_items.into_bump_slice(); let value: Expr<'a> = List(items.replace_items(new_items)); @@ -435,7 +489,8 @@ pub fn desugar_expr<'a>( }) } Record(fields) => { - let fields = desugar_field_collection(arena, *fields, src, line_info, module_path); + let fields = + desugar_field_collection(arena, *fields, src, line_info, module_path, problems); arena.alloc(Loc { region: loc_expr.region, value: Record(fields), @@ -444,7 +499,7 @@ pub fn desugar_expr<'a>( Tuple(fields) => { let mut allocated = Vec::with_capacity_in(fields.len(), arena); for field in fields.iter() { - let expr = desugar_expr(arena, field, src, line_info, module_path); + let expr = desugar_expr(arena, field, src, line_info, module_path, problems); allocated.push(expr); } let fields = fields.replace_items(allocated.into_bump_slice()); @@ -456,11 +511,12 @@ pub fn desugar_expr<'a>( RecordUpdate { fields, update } => { // NOTE the `update` field is always a `Var { .. }`, we only desugar it to get rid of // any spaces before/after - let new_update = desugar_expr(arena, update, src, line_info, module_path); + let new_update = desugar_expr(arena, update, src, line_info, module_path, problems); let mut allocated = Vec::with_capacity_in(fields.len(), arena); for field in fields.iter() { - let value = desugar_field(arena, &field.value, src, line_info, module_path); + let value = + desugar_field(arena, &field.value, src, line_info, module_path, problems); allocated.push(Loc { value, region: field.region, @@ -479,8 +535,8 @@ pub fn desugar_expr<'a>( Closure(loc_patterns, loc_ret) => arena.alloc(Loc { region: loc_expr.region, value: Closure( - desugar_loc_patterns(arena, loc_patterns, src, line_info, module_path), - desugar_expr(arena, loc_ret, src, line_info, module_path), + desugar_loc_patterns(arena, loc_patterns, src, line_info, module_path, problems), + desugar_expr(arena, loc_ret, src, line_info, module_path, problems), ), }), Backpassing(loc_patterns, loc_body, loc_ret) => { @@ -488,12 +544,19 @@ pub fn desugar_expr<'a>( // // loc_ret + let problem_region = Region::span_across( + &Region::across_all(loc_patterns.iter().map(|loc_pattern| &loc_pattern.region)), + &loc_body.region, + ); + problems.push(Problem::DeprecatedBackpassing(problem_region)); + // first desugar the body, because it may contain |> - let desugared_body = desugar_expr(arena, loc_body, src, line_info, module_path); + let desugared_body = + desugar_expr(arena, loc_body, src, line_info, module_path, problems); - let desugared_ret = desugar_expr(arena, loc_ret, src, line_info, module_path); + let desugared_ret = desugar_expr(arena, loc_ret, src, line_info, module_path, problems); let desugared_loc_patterns = - desugar_loc_patterns(arena, loc_patterns, src, line_info, module_path); + desugar_loc_patterns(arena, loc_patterns, src, line_info, module_path, problems); let closure = Expr::Closure(desugared_loc_patterns, desugared_ret); let loc_closure = Loc::at(loc_expr.region, closure); @@ -529,7 +592,7 @@ pub fn desugar_expr<'a>( RecordBuilder { mapper, fields } => { // NOTE the `mapper` is always a `Var { .. }`, we only desugar it to get rid of // any spaces before/after - let new_mapper = desugar_expr(arena, mapper, src, line_info, module_path); + let new_mapper = desugar_expr(arena, mapper, src, line_info, module_path, problems); if fields.is_empty() { return arena.alloc(Loc { @@ -553,7 +616,8 @@ pub fn desugar_expr<'a>( for field in fields.items { let (name, value, ignored) = - match desugar_field(arena, &field.value, src, line_info, module_path) { + match desugar_field(arena, &field.value, src, line_info, module_path, problems) + { AssignedField::RequiredValue(loc_name, _, loc_val) => { (loc_name, loc_val, false) } @@ -791,11 +855,20 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, ), Defs(defs, loc_ret) => { let mut defs = (*defs).clone(); - desugar_defs_node_values(arena, &mut defs, src, line_info, module_path, false); - let loc_ret = desugar_expr(arena, loc_ret, src, line_info, module_path); + desugar_defs_node_values( + arena, + &mut defs, + src, + line_info, + module_path, + false, + problems, + ); + let loc_ret = desugar_expr(arena, loc_ret, src, line_info, module_path, problems); arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret))) } @@ -827,14 +900,21 @@ pub fn desugar_expr<'a>( } }; - desugared_args.push(desugar_expr(arena, arg, src, line_info, module_path)); + desugared_args.push(desugar_expr( + arena, + arg, + src, + line_info, + module_path, + problems, + )); } let desugared_args = desugared_args.into_bump_slice(); let mut apply: &Loc = arena.alloc(Loc { value: Apply( - desugar_expr(arena, loc_fn, src, line_info, module_path), + desugar_expr(arena, loc_fn, src, line_info, module_path, problems), desugared_args, *called_via, ), @@ -846,7 +926,8 @@ pub fn desugar_expr<'a>( Some(apply_exprs) => { for expr in apply_exprs { - let desugared_expr = desugar_expr(arena, expr, src, line_info, module_path); + let desugared_expr = + desugar_expr(arena, expr, src, line_info, module_path, problems); let args = std::slice::from_ref(arena.alloc(apply)); @@ -867,17 +948,31 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, )); let mut desugared_branches = Vec::with_capacity_in(branches.len(), arena); for branch in branches.iter() { let desugared_expr = - desugar_expr(arena, &branch.value, src, line_info, module_path); - let desugared_patterns = - desugar_loc_patterns(arena, branch.patterns, src, line_info, module_path); + desugar_expr(arena, &branch.value, src, line_info, module_path, problems); + let desugared_patterns = desugar_loc_patterns( + arena, + branch.patterns, + src, + line_info, + module_path, + problems, + ); let desugared_guard = if let Some(guard) = &branch.guard { - Some(*desugar_expr(arena, guard, src, line_info, module_path)) + Some(*desugar_expr( + arena, + guard, + src, + line_info, + module_path, + problems, + )) } else { None }; @@ -915,8 +1010,14 @@ pub fn desugar_expr<'a>( }, }; let loc_fn_var = arena.alloc(Loc { region, value }); - let desugared_args = - arena.alloc([desugar_expr(arena, loc_arg, src, line_info, module_path)]); + let desugared_args = arena.alloc([desugar_expr( + arena, + loc_arg, + src, + line_info, + module_path, + problems, + )]); arena.alloc(Loc { value: Apply(loc_fn_var, desugared_args, CalledVia::UnaryOp(op)), @@ -935,6 +1036,7 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, ) } ParensAround(expr) => { @@ -947,6 +1049,7 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, ); arena.alloc(Loc { @@ -962,14 +1065,15 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, )); let mut desugared_if_thens = Vec::with_capacity_in(if_thens.len(), arena); for (condition, then_branch) in if_thens.iter() { desugared_if_thens.push(( - *desugar_expr(arena, condition, src, line_info, module_path), - *desugar_expr(arena, then_branch, src, line_info, module_path), + *desugar_expr(arena, condition, src, line_info, module_path, problems), + *desugar_expr(arena, then_branch, src, line_info, module_path, problems), )); } @@ -979,14 +1083,21 @@ pub fn desugar_expr<'a>( }) } Expect(condition, continuation) => { - let desugared_condition = - &*arena.alloc(desugar_expr(arena, condition, src, line_info, module_path)); + let desugared_condition = &*arena.alloc(desugar_expr( + arena, + condition, + src, + line_info, + module_path, + problems, + )); let desugared_continuation = &*arena.alloc(desugar_expr( arena, continuation, src, line_info, module_path, + problems, )); arena.alloc(Loc { value: Expect(desugared_condition, desugared_continuation), @@ -1002,6 +1113,7 @@ pub fn desugar_expr<'a>( src, line_info, module_path, + problems, )); let region = condition.region; @@ -1014,8 +1126,14 @@ pub fn desugar_expr<'a>( value: inspect_fn, region, }); - let desugared_inspect_args = - arena.alloc([desugar_expr(arena, condition, src, line_info, module_path)]); + let desugared_inspect_args = arena.alloc([desugar_expr( + arena, + condition, + src, + line_info, + module_path, + problems, + )]); let dbg_str = arena.alloc(Loc { value: Apply(loc_inspect_fn_var, desugared_inspect_args, CalledVia::Space), @@ -1060,6 +1178,7 @@ fn desugar_str_segments<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> &'a [StrSegment<'a>] { Vec::from_iter_in( segments.iter().map(|segment| match segment { @@ -1076,6 +1195,7 @@ fn desugar_str_segments<'a>( src, line_info, module_path, + problems, ); StrSegment::DeprecatedInterpolated(Loc { region: loc_desugared.region, @@ -1092,6 +1212,7 @@ fn desugar_str_segments<'a>( src, line_info, module_path, + problems, ); StrSegment::Interpolated(Loc { region: loc_desugared.region, @@ -1110,11 +1231,12 @@ fn desugar_field_collection<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> Collection<'a, Loc>>> { let mut allocated = Vec::with_capacity_in(fields.len(), arena); for field in fields.iter() { - let value = desugar_field(arena, &field.value, src, line_info, module_path); + let value = desugar_field(arena, &field.value, src, line_info, module_path, problems); allocated.push(Loc::at(field.region, value)); } @@ -1128,6 +1250,7 @@ fn desugar_field<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> AssignedField<'a, Expr<'a>> { use roc_parse::ast::AssignedField::*; @@ -1138,7 +1261,7 @@ fn desugar_field<'a>( region: loc_str.region, }, spaces, - desugar_expr(arena, loc_expr, src, line_info, module_path), + desugar_expr(arena, loc_expr, src, line_info, module_path, problems), ), OptionalValue(loc_str, spaces, loc_expr) => OptionalValue( Loc { @@ -1146,7 +1269,7 @@ fn desugar_field<'a>( region: loc_str.region, }, spaces, - desugar_expr(arena, loc_expr, src, line_info, module_path), + desugar_expr(arena, loc_expr, src, line_info, module_path, problems), ), IgnoredValue(loc_str, spaces, loc_expr) => IgnoredValue( Loc { @@ -1154,7 +1277,7 @@ fn desugar_field<'a>( region: loc_str.region, }, spaces, - desugar_expr(arena, loc_expr, src, line_info, module_path), + desugar_expr(arena, loc_expr, src, line_info, module_path, problems), ), LabelOnly(loc_str) => { // Desugar { x } into { x: x } @@ -1172,11 +1295,22 @@ fn desugar_field<'a>( region: loc_str.region, }, &[], - desugar_expr(arena, arena.alloc(loc_expr), src, line_info, module_path), + desugar_expr( + arena, + arena.alloc(loc_expr), + src, + line_info, + module_path, + problems, + ), ) } - SpaceBefore(field, _spaces) => desugar_field(arena, field, src, line_info, module_path), - SpaceAfter(field, _spaces) => desugar_field(arena, field, src, line_info, module_path), + SpaceBefore(field, _spaces) => { + desugar_field(arena, field, src, line_info, module_path, problems) + } + SpaceAfter(field, _spaces) => { + desugar_field(arena, field, src, line_info, module_path, problems) + } Malformed(string) => Malformed(string), } @@ -1188,11 +1322,19 @@ fn desugar_loc_patterns<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> &'a [Loc>] { Vec::from_iter_in( loc_patterns.iter().map(|loc_pattern| Loc { region: loc_pattern.region, - value: desugar_pattern(arena, loc_pattern.value, src, line_info, module_path), + value: desugar_pattern( + arena, + loc_pattern.value, + src, + line_info, + module_path, + problems, + ), }), arena, ) @@ -1205,10 +1347,18 @@ fn desugar_loc_pattern<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> &'a Loc> { arena.alloc(Loc { region: loc_pattern.region, - value: desugar_pattern(arena, loc_pattern.value, src, line_info, module_path), + value: desugar_pattern( + arena, + loc_pattern.value, + src, + line_info, + module_path, + problems, + ), }) } @@ -1218,6 +1368,7 @@ fn desugar_pattern<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> Pattern<'a> { use roc_parse::ast::Pattern::*; @@ -1241,7 +1392,14 @@ fn desugar_pattern<'a>( let desugared_arg_patterns = Vec::from_iter_in( arg_patterns.iter().map(|arg_pattern| Loc { region: arg_pattern.region, - value: desugar_pattern(arena, arg_pattern.value, src, line_info, module_path), + value: desugar_pattern( + arena, + arg_pattern.value, + src, + line_info, + module_path, + problems, + ), }), arena, ) @@ -1252,8 +1410,14 @@ fn desugar_pattern<'a>( RecordDestructure(field_patterns) => { let mut allocated = Vec::with_capacity_in(field_patterns.len(), arena); for field_pattern in field_patterns.iter() { - let value = - desugar_pattern(arena, field_pattern.value, src, line_info, module_path); + let value = desugar_pattern( + arena, + field_pattern.value, + src, + line_info, + module_path, + problems, + ); allocated.push(Loc { value, region: field_pattern.region, @@ -1265,15 +1429,17 @@ fn desugar_pattern<'a>( } RequiredField(name, field_pattern) => RequiredField( name, - desugar_loc_pattern(arena, field_pattern, src, line_info, module_path), + desugar_loc_pattern(arena, field_pattern, src, line_info, module_path, problems), + ), + OptionalField(name, expr) => OptionalField( + name, + desugar_expr(arena, expr, src, line_info, module_path, problems), ), - OptionalField(name, expr) => { - OptionalField(name, desugar_expr(arena, expr, src, line_info, module_path)) - } Tuple(patterns) => { let mut allocated = Vec::with_capacity_in(patterns.len(), arena); for pattern in patterns.iter() { - let value = desugar_pattern(arena, pattern.value, src, line_info, module_path); + let value = + desugar_pattern(arena, pattern.value, src, line_info, module_path, problems); allocated.push(Loc { value, region: pattern.region, @@ -1286,7 +1452,8 @@ fn desugar_pattern<'a>( List(patterns) => { let mut allocated = Vec::with_capacity_in(patterns.len(), arena); for pattern in patterns.iter() { - let value = desugar_pattern(arena, pattern.value, src, line_info, module_path); + let value = + desugar_pattern(arena, pattern.value, src, line_info, module_path, problems); allocated.push(Loc { value, region: pattern.region, @@ -1297,14 +1464,14 @@ fn desugar_pattern<'a>( List(patterns) } As(sub_pattern, symbol) => As( - desugar_loc_pattern(arena, sub_pattern, src, line_info, module_path), + desugar_loc_pattern(arena, sub_pattern, src, line_info, module_path, problems), symbol, ), SpaceBefore(sub_pattern, _spaces) => { - desugar_pattern(arena, *sub_pattern, src, line_info, module_path) + desugar_pattern(arena, *sub_pattern, src, line_info, module_path, problems) } SpaceAfter(sub_pattern, _spaces) => { - desugar_pattern(arena, *sub_pattern, src, line_info, module_path) + desugar_pattern(arena, *sub_pattern, src, line_info, module_path, problems) } } } @@ -1425,6 +1592,7 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) { } } +#[allow(clippy::too_many_arguments)] fn desugar_bin_ops<'a>( arena: &'a Bump, whole_region: Region, @@ -1433,19 +1601,27 @@ fn desugar_bin_ops<'a>( src: &'a str, line_info: &mut Option, module_path: &str, + problems: &mut std::vec::Vec, ) -> &'a Loc> { let mut arg_stack: Vec<&'a Loc> = Vec::with_capacity_in(lefts.len() + 1, arena); let mut op_stack: Vec> = Vec::with_capacity_in(lefts.len(), arena); for (loc_expr, loc_op) in lefts { - arg_stack.push(desugar_expr(arena, loc_expr, src, line_info, module_path)); + arg_stack.push(desugar_expr( + arena, + loc_expr, + src, + line_info, + module_path, + problems, + )); match run_binop_step(arena, whole_region, &mut arg_stack, &mut op_stack, *loc_op) { Err(problem) => return problem, Ok(()) => continue, } } - let mut expr = desugar_expr(arena, right, src, line_info, module_path); + let mut expr = desugar_expr(arena, right, src, line_info, module_path, problems); for (left, loc_op) in arg_stack.into_iter().zip(op_stack.into_iter()).rev() { expr = arena.alloc(new_op_call_expr(arena, left, loc_op, expr)); diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index ddcbcf43613..6a13626e30c 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -337,7 +337,15 @@ pub fn canonicalize_module_defs<'a>( // operators, and then again on *their* nested operators, ultimately applying the // rules multiple times unnecessarily. - crate::desugar::desugar_defs_node_values(arena, loc_defs, src, &mut None, module_path, true); + crate::desugar::desugar_defs_node_values( + arena, + loc_defs, + src, + &mut None, + module_path, + true, + &mut env.problems, + ); let mut rigid_variables = RigidVariables::default(); diff --git a/crates/compiler/can/tests/helpers/mod.rs b/crates/compiler/can/tests/helpers/mod.rs index 508d84ea841..c1e144c7d20 100644 --- a/crates/compiler/can/tests/helpers/mod.rs +++ b/crates/compiler/can/tests/helpers/mod.rs @@ -59,6 +59,7 @@ pub fn can_expr_with(arena: &Bump, home: ModuleId, expr_str: &str) -> CanExprOut expr_str, &mut None, arena.alloc("TestPath"), + &mut Default::default(), ); let mut scope = Scope::new( diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs index 48ac9fb91ca..f82d857e5f3 100644 --- a/crates/compiler/can/tests/test_suffixed.rs +++ b/crates/compiler/can/tests/test_suffixed.rs @@ -12,7 +12,15 @@ mod suffixed_tests { ($src:expr) => {{ let arena = &Bump::new(); let mut defs = parse_defs_with(arena, indoc!($src)).unwrap(); - desugar_defs_node_values(arena, &mut defs, $src, &mut None, "test.roc", true); + desugar_defs_node_values( + arena, + &mut defs, + $src, + &mut None, + "test.roc", + true, + &mut Default::default(), + ); let snapshot = format!("{:#?}", &defs); println!("{}", snapshot); diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 63679c98208..4c86b54c8ab 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -174,6 +174,7 @@ pub fn can_expr_with<'a>( expr_str, &mut None, arena.alloc("TestPath"), + &mut Default::default(), ); let mut scope = Scope::new( diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 96fd1aa33c8..ac4bbc391ef 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -4816,7 +4816,7 @@ mod test_reporting { expression_indentation_end, indoc!( r" - f <- Foo.foo + f = Foo.foo " ), @r#" @@ -4827,8 +4827,8 @@ mod test_reporting { 1│ app "test" provides [main] to "./platform" 2│ 3│ main = - 4│ f <- Foo.foo - ^ + 4│ f = Foo.foo + ^ Looks like the indentation ends prematurely here. Did you mean to have another expression after this line? @@ -6617,34 +6617,6 @@ All branches in an `if` must have the same type! " ); - test_report!( - backpassing_type_error, - indoc!( - r#" - x <- List.map ["a", "b"] - - x + 1 - "# - ), - @r#" - ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── - - This 2nd argument to `map` has an unexpected type: - - 4│> x <- List.map ["a", "b"] - 5│> - 6│> x + 1 - - The argument is an anonymous function of type: - - Num * -> Num * - - But `map` needs its 2nd argument to be: - - Str -> Num * - "# - ); - test_report!( expect_expr_type_error, indoc!( @@ -10198,9 +10170,9 @@ All branches in an `if` must have the same type! withOpen : (Handle -> Result {} *) -> Result {} * withOpen = \callback -> - handle <- await (open {}) - {} <- await (callback handle) - close handle + await (open {}) \handle -> + await (callback handle) \_ -> + close handle withOpen " @@ -10212,9 +10184,9 @@ All branches in an `if` must have the same type! 10│ withOpen : (Handle -> Result {} *) -> Result {} * 11│ withOpen = \callback -> - 12│> handle <- await (open {}) - 13│> {} <- await (callback handle) - 14│> close handle + 12│> await (open {}) \handle -> + 13│> await (callback handle) \_ -> + 14│> close handle The type annotation on `withOpen` says this `await` call should have the type: @@ -10227,6 +10199,7 @@ All branches in an `if` must have the same type! Tip: Any connection between types must use a named type variable, not a `*`! Maybe the annotation on `withOpen` should have a named type variable in place of the `*`? + " ); @@ -10830,7 +10803,7 @@ All branches in an `if` must have the same type! 7│ a: <- "a", ^^^ - Tip: Remove `<-` to assign the field directly. + Tip: Remove <- to assign the field directly. "# ); @@ -10962,7 +10935,7 @@ All branches in an `if` must have the same type! 6│ { xyz <- ^^^ - Note: Record builders need a mapper function before the `<-` to combine + Note: Record builders need a mapper function before the <- to combine fields together with. "# ); @@ -11844,6 +11817,32 @@ All branches in an `if` must have the same type! @r" " ); + + test_report!( + deprecated_backpassing, + indoc!( + r#" + foo = \bar -> + baz <- Result.try bar + + Ok (baz * 3) + + foo (Ok 123) + "# + ), + @r###" + ── BACKPASSING DEPRECATED in /code/proj/Main.roc ─────────────────────────────── + + Backpassing (<-) like this will soon be deprecated: + + 5│ baz <- Result.try bar + ^^^^^^^^^^^^^^^^^^^^^ + + You should use a ! for awaiting tasks or a ? for trying results, and + functions everywhere else. + "### + ); + test_report!( unknown_shorthand_no_deps, indoc!( @@ -13881,7 +13880,7 @@ All branches in an `if` must have the same type! "# ), @r#" - ── DEFINITIONs ONLY USED IN RECURSION in /code/proj/Main.roc ─────────────────── + ── DEFINITIONS ONLY USED IN RECURSION in /code/proj/Main.roc ─────────────────── These 2 definitions are only used in mutual recursion with themselves: @@ -13890,6 +13889,7 @@ All branches in an `if` must have the same type! If you don't intend to use or export any of them, they should all be removed! + "# ); @@ -13953,7 +13953,7 @@ All branches in an `if` must have the same type! "# ), @r#" - ── DEFINITIONs ONLY USED IN RECURSION in /code/proj/Main.roc ─────────────────── + ── DEFINITIONS ONLY USED IN RECURSION in /code/proj/Main.roc ─────────────────── These 2 definitions are only used in mutual recursion with themselves: @@ -13962,6 +13962,7 @@ All branches in an `if` must have the same type! If you don't intend to use or export any of them, they should all be removed! + "# ); diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index cfac8d5f2a3..18c274020a9 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -118,6 +118,23 @@ impl std::fmt::Display for UnaryOp { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Suffix { + /// (!), e.g. (Stdin.line!) + Bang, + /// (?), e.g. (parseData? data) + Question, +} + +impl std::fmt::Display for Suffix { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Suffix::Bang => write!(f, "!"), + Suffix::Question => write!(f, "?"), + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BinOp { // highest precedence diff --git a/crates/compiler/problem/src/can.rs b/crates/compiler/problem/src/can.rs index b253dcab3df..5f2463dea8d 100644 --- a/crates/compiler/problem/src/can.rs +++ b/crates/compiler/problem/src/can.rs @@ -54,6 +54,7 @@ pub enum Problem { new_symbol: Symbol, existing_symbol_region: Region, }, + DeprecatedBackpassing(Region), /// First symbol is the name of the closure with that argument /// Bool is whether the closure is anonymous /// Second symbol is the name of the argument that is unused @@ -257,6 +258,7 @@ impl Problem { Problem::ExplicitBuiltinImport(_, _) => Warning, Problem::ExplicitBuiltinTypeImport(_, _) => Warning, Problem::ImportShadowsSymbol { .. } => RuntimeError, + Problem::DeprecatedBackpassing(_) => Warning, Problem::ExposedButNotDefined(_) => RuntimeError, Problem::UnknownGeneratesWith(_) => RuntimeError, Problem::UnusedArgument(_, _, _, _) => Warning, @@ -340,6 +342,7 @@ impl Problem { | Problem::ExplicitBuiltinImport(_, region) | Problem::ExplicitBuiltinTypeImport(_, region) | Problem::ImportShadowsSymbol { region, .. } + | Problem::DeprecatedBackpassing(region) | Problem::UnknownGeneratesWith(Loc { region, .. }) | Problem::UnusedArgument(_, _, _, region) | Problem::UnusedBranchDef(_, region) diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 9a0a4f28463..5e8e78ed0a1 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -3389,8 +3389,8 @@ fn inspect_custom_type() { myToInspector : HelloWorld -> Inspector f where f implements InspectFormatter myToInspector = \@HellowWorld {} -> - fmt <- Inspect.custom - Inspect.apply (Inspect.str "Hello, World!\n") fmt + Inspect.custom \fmt -> + Inspect.apply (Inspect.str "Hello, World!\n") fmt main = Inspect.inspect (@HelloWorld {}) diff --git a/crates/reporting/src/error/canonicalize.rs b/crates/reporting/src/error/canonicalize.rs index 01d04a44be6..b42e022b7e1 100644 --- a/crates/reporting/src/error/canonicalize.rs +++ b/crates/reporting/src/error/canonicalize.rs @@ -1,4 +1,5 @@ use roc_collections::all::MutSet; +use roc_module::called_via::Suffix; use roc_module::ident::{Ident, Lowercase, ModuleName}; use roc_module::symbol::DERIVABLE_ABILITIES; use roc_problem::can::PrecedenceProblem::BothNonAssociative; @@ -246,6 +247,26 @@ pub fn can_problem<'b>( title = DUPLICATE_NAME.to_string(); } + Problem::DeprecatedBackpassing(region) => { + doc = alloc.stack([ + alloc.concat([ + alloc.reflow("Backpassing ("), + alloc.backpassing_arrow(), + alloc.reflow(") like this will soon be deprecated:"), + ]), + alloc.region(lines.convert_region(region), severity), + alloc.concat([ + alloc.reflow("You should use a "), + alloc.suffix(Suffix::Bang), + alloc.reflow(" for awaiting tasks or a "), + alloc.suffix(Suffix::Question), + alloc.reflow(" for trying results, and functions everywhere else."), + ]), + ]); + + title = "BACKPASSING DEPRECATED".to_string(); + } + Problem::DefsOnlyUsedInRecursion(1, region) => { doc = alloc.stack([ alloc.reflow("This definition is only used in recursion with itself:"), @@ -270,7 +291,7 @@ pub fn can_problem<'b>( ), ]); - title = "DEFINITIONs ONLY USED IN RECURSION".to_string(); + title = "DEFINITIONS ONLY USED IN RECURSION".to_string(); } Problem::ExposedButNotDefined(symbol) => { doc = alloc.stack([ diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index e3c18b5bf36..1c35eccb901 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -1265,7 +1265,7 @@ fn to_expr_report<'b>( alloc.concat([ alloc.tip(), alloc.reflow("Remove "), - alloc.keyword("<-"), + alloc.backpassing_arrow(), alloc.reflow(" to assign the field directly.") ]) } @@ -1273,7 +1273,7 @@ fn to_expr_report<'b>( alloc.concat([ alloc.note(""), alloc.reflow("Record builders need a mapper function before the "), - alloc.keyword("<-"), + alloc.backpassing_arrow(), alloc.reflow(" to combine fields together with.") ]) } diff --git a/crates/reporting/src/report.rs b/crates/reporting/src/report.rs index 0e9e2d968d7..e914fccce45 100644 --- a/crates/reporting/src/report.rs +++ b/crates/reporting/src/report.rs @@ -509,6 +509,10 @@ impl<'a> RocDocAllocator<'a> { self.text(name).annotate(Annotation::Shorthand) } + pub fn backpassing_arrow(&'a self) -> DocBuilder<'a, Self, Annotation> { + self.text("<-").annotate(Annotation::BinOp) + } + pub fn binop( &'a self, content: roc_module::called_via::BinOp, @@ -523,6 +527,13 @@ impl<'a> RocDocAllocator<'a> { self.text(content.to_string()).annotate(Annotation::UnaryOp) } + pub fn suffix( + &'a self, + content: roc_module::called_via::Suffix, + ) -> DocBuilder<'a, Self, Annotation> { + self.text(content.to_string()).annotate(Annotation::UnaryOp) + } + /// Turns off backticks/colors in a block pub fn type_block( &'a self, diff --git a/examples/Community.roc b/examples/Community.roc index f413f133e59..e19906a8f66 100644 --- a/examples/Community.roc +++ b/examples/Community.roc @@ -55,27 +55,28 @@ addFriend = \@Community { people, friends }, from, to -> walkFriendNames : Community, state, (state, Str, Set Str -> state) -> state walkFriendNames = \@Community { people, friends }, s0, nextFn -> (out, _) = - (s1, id), friendSet <- List.walk friends (s0, 0) - (@Person person) = - when List.get people id is - Ok v -> v - Err _ -> crash "Unknown Person" - personName = - person.firstName - |> Str.concat " " - |> Str.concat person.lastName - - friendNames = - friendsSet, friendId <- Set.walk friendSet (Set.empty {}) - (@Person friend) = - when List.get people friendId is + List.walk friends (s0, 0) \(s1, id), friendSet -> + (@Person person) = + when List.get people id is Ok v -> v Err _ -> crash "Unknown Person" - friendName = - friend.firstName + personName = + person.firstName |> Str.concat " " - |> Str.concat friend.lastName - Set.insert friendsSet friendName + |> Str.concat person.lastName + + friendNames = + Set.walk friendSet (Set.empty {}) \friendsSet, friendId -> + (@Person friend) = + when List.get people friendId is + Ok v -> v + Err _ -> crash "Unknown Person" + friendName = + friend.firstName + |> Str.concat " " + |> Str.concat friend.lastName + Set.insert friendsSet friendName + + (nextFn s1 personName friendNames, id + 1) - (nextFn s1 personName friendNames, id + 1) out diff --git a/examples/cli/false-interpreter/Context.roc b/examples/cli/false-interpreter/Context.roc index 747124a3342..1b97242f694 100644 --- a/examples/cli/false-interpreter/Context.roc +++ b/examples/cli/false-interpreter/Context.roc @@ -61,18 +61,18 @@ toStr = \{ scopes, stack, state, vars } -> with : Str, (Context -> Task {} a) -> Task {} a with = \path, callback -> - handle <- File.withOpen path - # I cant define scope here and put it in the list in callback. It breaks alias anaysis. - # Instead I have to inline this. - # root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None } - callback { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount } + File.withOpen path \handle -> + # I cant define scope here and put it in the list in callback. It breaks alias anaysis. + # Instead I have to inline this. + # root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None } + callback { scopes: [{ data: Some handle, index: 0, buf: [], whileInfo: None }], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount } # I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is. getChar : Context -> Task [T U8 Context] [EndOfData, NoScope] getChar = \ctx -> when List.last ctx.scopes is Ok scope -> - (T val newScope) <- Task.await (getCharScope scope) + (T val newScope) = getCharScope! scope Task.succeed (T val { ctx & scopes: List.set ctx.scopes (List.len ctx.scopes - 1) newScope }) Err ListWasEmpty -> @@ -87,7 +87,7 @@ getCharScope = \scope -> Err OutOfBounds -> when scope.data is Some h -> - bytes <- Task.await (File.chunk h) + bytes = File.chunk! h when List.first bytes is Ok val -> # This starts at 1 because the first character is already being returned. diff --git a/examples/cli/false-interpreter/False.roc b/examples/cli/false-interpreter/False.roc index 7300abff728..96fc3bb625a 100644 --- a/examples/cli/false-interpreter/False.roc +++ b/examples/cli/false-interpreter/False.roc @@ -28,47 +28,47 @@ main = \filename -> interpretFile : Str -> Task {} [StringErr Str] interpretFile = \filename -> - ctx <- Context.with filename - result <- Task.attempt (interpretCtx ctx) - when result is - Ok _ -> - Task.succeed {} + Context.with filename \ctx -> + result = interpretCtx ctx |> Task.result! + when result is + Ok _ -> + Task.succeed {} - Err BadUtf8 -> - Task.fail (StringErr "Failed to convert string from Utf8 bytes") + Err BadUtf8 -> + Task.fail (StringErr "Failed to convert string from Utf8 bytes") - Err DivByZero -> - Task.fail (StringErr "Division by zero") + Err DivByZero -> + Task.fail (StringErr "Division by zero") - Err EmptyStack -> - Task.fail (StringErr "Tried to pop a value off of the stack when it was empty") + Err EmptyStack -> + Task.fail (StringErr "Tried to pop a value off of the stack when it was empty") - Err InvalidBooleanValue -> - Task.fail (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)") + Err InvalidBooleanValue -> + Task.fail (StringErr "Ran into an invalid boolean that was neither false (0) or true (-1)") - Err (InvalidChar char) -> - Task.fail (StringErr "Ran into an invalid character with ascii code: $(char)") + Err (InvalidChar char) -> + Task.fail (StringErr "Ran into an invalid character with ascii code: $(char)") - Err MaxInputNumber -> - Task.fail (StringErr "Like the original false compiler, the max input number is 320,000") + Err MaxInputNumber -> + Task.fail (StringErr "Like the original false compiler, the max input number is 320,000") - Err NoLambdaOnStack -> - Task.fail (StringErr "Tried to run a lambda when no lambda was on the stack") + Err NoLambdaOnStack -> + Task.fail (StringErr "Tried to run a lambda when no lambda was on the stack") - Err NoNumberOnStack -> - Task.fail (StringErr "Tried to run a number when no number was on the stack") + Err NoNumberOnStack -> + Task.fail (StringErr "Tried to run a number when no number was on the stack") - Err NoVariableOnStack -> - Task.fail (StringErr "Tried to load a variable when no variable was on the stack") + Err NoVariableOnStack -> + Task.fail (StringErr "Tried to load a variable when no variable was on the stack") - Err NoScope -> - Task.fail (StringErr "Tried to run code when not in any scope") + Err NoScope -> + Task.fail (StringErr "Tried to run code when not in any scope") - Err OutOfBounds -> - Task.fail (StringErr "Tried to load from an offset that was outside of the stack") + Err OutOfBounds -> + Task.fail (StringErr "Tried to load from an offset that was outside of the stack") - Err UnexpectedEndOfData -> - Task.fail (StringErr "Hit end of data while still parsing something") + Err UnexpectedEndOfData -> + Task.fail (StringErr "Hit end of data while still parsing something") isDigit : U8 -> Bool isDigit = \char -> @@ -129,11 +129,11 @@ interpretCtxLoop = \ctx -> Task.fail NoScope Executing -> - # {} <- Task.await (Stdout.line (Context.toStr ctx)) - result <- Task.attempt (Context.getChar ctx) + # Stdout.line! (Context.toStr ctx) + result = Context.getChar ctx |> Task.result! when result is Ok (T val newCtx) -> - execCtx <- Task.await (stepExecCtx newCtx val) + execCtx = stepExecCtx! newCtx val Task.succeed (Step execCtx) Err NoScope -> @@ -151,7 +151,7 @@ interpretCtxLoop = \ctx -> Task.succeed (Step dropCtx) InComment -> - result <- Task.attempt (Context.getChar ctx) + result = Context.getChar ctx |> Task.result! when result is Ok (T val newCtx) -> if val == 0x7D then @@ -167,7 +167,7 @@ interpretCtxLoop = \ctx -> Task.fail UnexpectedEndOfData InNumber accum -> - result <- Task.attempt (Context.getChar ctx) + result = Context.getChar ctx |> Task.result! when result is Ok (T val newCtx) -> if isDigit val then @@ -182,7 +182,7 @@ interpretCtxLoop = \ctx -> # outside of number now, this needs to be executed. pushCtx = Context.pushStack newCtx (Number accum) - execCtx <- Task.await (stepExecCtx { pushCtx & state: Executing } val) + execCtx = stepExecCtx! { pushCtx & state: Executing } val Task.succeed (Step execCtx) Err NoScope -> @@ -192,14 +192,14 @@ interpretCtxLoop = \ctx -> Task.fail UnexpectedEndOfData InString bytes -> - result <- Task.attempt (Context.getChar ctx) + result = Context.getChar ctx |> Task.result! when result is Ok (T val newCtx) -> if val == 0x22 then # `"` end of string when Str.fromUtf8 bytes is Ok str -> - {} <- Task.await (Stdout.raw str) + Stdout.raw! str Task.succeed (Step { newCtx & state: Executing }) Err _ -> @@ -214,7 +214,7 @@ interpretCtxLoop = \ctx -> Task.fail UnexpectedEndOfData InLambda depth bytes -> - result <- Task.attempt (Context.getChar ctx) + result = Context.getChar ctx |> Task.result! when result is Ok (T val newCtx) -> if val == 0x5B then @@ -238,7 +238,7 @@ interpretCtxLoop = \ctx -> Task.fail UnexpectedEndOfData InSpecialChar -> - result <- Task.attempt (Context.getChar { ctx & state: Executing }) + result = Context.getChar { ctx & state: Executing } |> Task.result! when result is Ok (T 0xB8 newCtx) -> result2 = @@ -273,7 +273,7 @@ interpretCtxLoop = \ctx -> Task.fail UnexpectedEndOfData LoadChar -> - result <- Task.attempt (Context.getChar { ctx & state: Executing }) + result = Context.getChar { ctx & state: Executing } |> Task.result! when result is Ok (T x newCtx) -> Task.succeed (Step (Context.pushStack newCtx (Number (Num.intCast x)))) @@ -472,7 +472,7 @@ stepExecCtx = \ctx, char -> Ok (T popCtx num) -> when Str.fromUtf8 [Num.intCast num] is Ok str -> - {} <- Task.await (Stdout.raw str) + Stdout.raw! str Task.succeed popCtx Err _ -> @@ -485,7 +485,7 @@ stepExecCtx = \ctx, char -> # `.` write int when popNumber ctx is Ok (T popCtx num) -> - {} <- Task.await (Stdout.raw (Num.toStr (Num.intCast num))) + Stdout.raw! (Num.toStr (Num.intCast num)) Task.succeed popCtx Err e -> @@ -493,7 +493,7 @@ stepExecCtx = \ctx, char -> 0x5E -> # `^` read char as int - in <- Task.await Stdin.char + in = Stdin.char! if in == 255 then # max char sent on EOF. Change to -1 Task.succeed (Context.pushStack ctx (Number -1)) diff --git a/examples/cli/false-interpreter/platform/File.roc b/examples/cli/false-interpreter/platform/File.roc index 9d6e2e86772..d9496bc4552 100644 --- a/examples/cli/false-interpreter/platform/File.roc +++ b/examples/cli/false-interpreter/platform/File.roc @@ -22,7 +22,8 @@ close = \@Handle handle -> Effect.after (Effect.closeFile handle) Task.succeed withOpen : Str, (Handle -> Task {} a) -> Task {} a withOpen = \path, callback -> - handle <- Task.await (open path) - result <- Task.attempt (callback handle) - {} <- Task.await (close handle) + handle = open! path + result = callback handle |> Task.result! + close! handle + Task.fromResult result diff --git a/examples/cli/false-interpreter/platform/Task.roc b/examples/cli/false-interpreter/platform/Task.roc index d5f212da476..bdc608f2790 100644 --- a/examples/cli/false-interpreter/platform/Task.roc +++ b/examples/cli/false-interpreter/platform/Task.roc @@ -1,4 +1,14 @@ -module [Task, succeed, fail, await, map, onFail, attempt, fromResult, loop] +module [ + Task, + succeed, + fail, + await, + map, + onFail, + attempt, + fromResult, + loop, +] import pf.Effect @@ -66,3 +76,9 @@ map = \effect, transform -> when result is Ok a -> Task.succeed (transform a) Err err -> Task.fail err + +result : Task ok err -> Task (Result ok err) * +result = \effect -> + Effect.after + effect + \result -> Task.succeed result diff --git a/examples/virtual-dom-wip/platform/Html/Internal/Client.roc b/examples/virtual-dom-wip/platform/Html/Internal/Client.roc index 00e42d1fd4a..46ca2549c47 100644 --- a/examples/virtual-dom-wip/platform/Html/Internal/Client.roc +++ b/examples/virtual-dom-wip/platform/Html/Internal/Client.roc @@ -88,13 +88,12 @@ initClientApp = \json, app -> initClientAppHelp json app # Call out to JS to patch the DOM, attaching the event listeners - _ <- applyPatches patches |> Effect.after - - Effect.always { - app, - state, - rendered, - } + Effect.after (applyPatches patches) \_ -> + Effect.always { + app, + state, + rendered, + } # Testable helper function to initialise the app initClientAppHelp : List U8, App state initData -> { state, rendered : RenderedTree state, patches : List Patch } where initData implements Decoding @@ -222,16 +221,16 @@ dispatchEvent = \platformState, eventData, handlerId -> { rendered: newRendered, patches } = diff { rendered, patches: [] } newViewUnrendered - _ <- applyPatches patches |> Effect.after - Effect.always { - platformState: { - app, - state: newState, - rendered: newRendered, - }, - stopPropagation, - preventDefault, - } + Effect.after (applyPatches patches) \_ -> + Effect.always { + platformState: { + app, + state: newState, + rendered: newRendered, + }, + stopPropagation, + preventDefault, + } None -> Effect.always { platformState, stopPropagation, preventDefault } diff --git a/examples/virtual-dom-wip/platform/Json.roc b/examples/virtual-dom-wip/platform/Json.roc index d28c2e925bc..1b1c456694d 100644 --- a/examples/virtual-dom-wip/platform/Json.roc +++ b/examples/virtual-dom-wip/platform/Json.roc @@ -540,32 +540,35 @@ expect decodeTuple = \initialState, stepElem, finalizer -> Decode.custom \initialBytes, @Json {} -> # NB: the stepper function must be passed explicitly until #2894 is resolved. decodeElems = \stepper, state, index, bytes -> - { val: newState, rest: beforeCommaOrBreak } <- tryDecode - ( - when stepper state index is - TooLong -> - { rest: beforeCommaOrBreak } <- bytes |> anything |> tryDecode - { result: Ok state, rest: beforeCommaOrBreak } - - Next decoder -> - Decode.decodeWith bytes decoder json - ) - - { result: commaResult, rest: nextBytes } = comma beforeCommaOrBreak - - when commaResult is - Ok {} -> decodeElems stepElem newState (index + 1) nextBytes - Err _ -> { result: Ok newState, rest: nextBytes } - - { rest: afterBracketBytes } <- initialBytes |> openBracket |> tryDecode - - { val: endStateResult, rest: beforeClosingBracketBytes } <- decodeElems stepElem initialState 0 afterBracketBytes |> tryDecode - - { rest: afterTupleBytes } <- beforeClosingBracketBytes |> closingBracket |> tryDecode - - when finalizer endStateResult is - Ok val -> { result: Ok val, rest: afterTupleBytes } - Err e -> { result: Err e, rest: afterTupleBytes } + ( + when stepper state index is + TooLong -> + bytes + |> anything + |> tryDecode \{ rest: beforeCommaOrBreak } -> + { result: Ok state, rest: beforeCommaOrBreak } + + Next decoder -> + Decode.decodeWith bytes decoder json + ) + |> tryDecode \{ val: newState, rest: beforeCommaOrBreak } -> + { result: commaResult, rest: nextBytes } = comma beforeCommaOrBreak + + when commaResult is + Ok {} -> decodeElems stepElem newState (index + 1) nextBytes + Err _ -> { result: Ok newState, rest: nextBytes } + + initialBytes + |> openBracket + |> tryDecode \{ rest: afterBracketBytes } -> + decodeElems stepElem initialState 0 afterBracketBytes + |> tryDecode \{ val: endStateResult, rest: beforeClosingBracketBytes } -> + beforeClosingBracketBytes + |> closingBracket + |> tryDecode \{ rest: afterTupleBytes } -> + when finalizer endStateResult is + Ok val -> { result: Ok val, rest: afterTupleBytes } + Err e -> { result: Err e, rest: afterTupleBytes } # Test decode of tuple expect @@ -1225,44 +1228,42 @@ decodeRecord = \initialState, stepField, finalizer -> Decode.custom \bytes, @Jso Ok objectName -> # Decode the json value - { val: updatedRecord, rest: bytesAfterValue } <- - ( - fieldName = objectName - - # Retrieve value decoder for the current field - when stepField recordState fieldName is - Skip -> - # TODO This doesn't seem right, shouldn't we eat - # the remaining json object value bytes if we are skipping this - # field? - { result: Ok recordState, rest: valueBytes } - - Keep valueDecoder -> - # Decode the value using the decoder from the recordState - # Note we need to pass json config options recursively here - Decode.decodeWith valueBytes valueDecoder (@Json {}) - ) - |> tryDecode - - # Check if another field or '}' for end of object - when List.walkUntil bytesAfterValue (AfterObjectValue 0) objectHelp is - ObjectFieldNameStart n -> - rest = List.dropFirst bytesAfterValue n - - # Decode the next field and value - decodeFields updatedRecord rest - - AfterClosingBrace n -> - rest = List.dropFirst bytesAfterValue n - - # Build final record from decoded fields and values - when finalizer updatedRecord json is - Ok val -> { result: Ok val, rest } - Err e -> { result: Err e, rest } - - _ -> - # Invalid object - { result: Err TooShort, rest: bytesAfterValue } + ( + fieldName = objectName + + # Retrieve value decoder for the current field + when stepField recordState fieldName is + Skip -> + # TODO This doesn't seem right, shouldn't we eat + # the remaining json object value bytes if we are skipping this + # field? + { result: Ok recordState, rest: valueBytes } + + Keep valueDecoder -> + # Decode the value using the decoder from the recordState + # Note we need to pass json config options recursively here + Decode.decodeWith valueBytes valueDecoder (@Json {}) + ) + |> tryDecode \{ val: updatedRecord, rest: bytesAfterValue } -> + # Check if another field or '}' for end of object + when List.walkUntil bytesAfterValue (AfterObjectValue 0) objectHelp is + ObjectFieldNameStart n -> + rest = List.dropFirst bytesAfterValue n + + # Decode the next field and value + decodeFields updatedRecord rest + + AfterClosingBrace n -> + rest = List.dropFirst bytesAfterValue n + + # Build final record from decoded fields and values + when finalizer updatedRecord json is + Ok val -> { result: Ok val, rest } + Err e -> { result: Err e, rest } + + _ -> + # Invalid object + { result: Err TooShort, rest: bytesAfterValue } countBytesBeforeFirstField = when List.walkUntil bytes (BeforeOpeningBrace 0) objectHelp is From f9008c3af0e992f20b60f706e34b4ae518df5993 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 23:37:31 -0700 Subject: [PATCH 191/203] Fix broken CLI tests --- crates/cli/tests/benchmarks/platform/Task.roc | 20 +++++++------- crates/cli/tests/cli_run.rs | 27 ++++++++++++------- crates/cli/tests/known_bad/TypeError.roc | 9 +++---- .../cli/false-interpreter/platform/Task.roc | 25 ++++++++--------- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/crates/cli/tests/benchmarks/platform/Task.roc b/crates/cli/tests/benchmarks/platform/Task.roc index d948e7d7987..090710395a0 100644 --- a/crates/cli/tests/benchmarks/platform/Task.roc +++ b/crates/cli/tests/benchmarks/platform/Task.roc @@ -38,7 +38,7 @@ loop = \state, step -> \res -> when res is Ok (Step newState) -> Step newState - Ok (Done result) -> Done (Ok result) + Ok (Done res2) -> Done (Ok res2) Err e -> Done (Err e) Effect.loop state looper @@ -55,8 +55,8 @@ after : Task a err, (a -> Task b err) -> Task b err after = \effect, transform -> Effect.after effect - \result -> - when result is + \res -> + when res is Ok a -> transform a Err err -> Task.fail err @@ -64,8 +64,8 @@ await : Task a err, (a -> Task b err) -> Task b err await = \effect, transform -> Effect.after effect - \result -> - when result is + \res -> + when res is Ok a -> transform a Err err -> Task.fail err @@ -73,8 +73,8 @@ attempt : Task a b, (Result a b -> Task c d) -> Task c d attempt = \task, transform -> Effect.after task - \result -> - when result is + \res -> + when res is Ok ok -> transform (Ok ok) Err err -> transform (Err err) @@ -82,8 +82,8 @@ map : Task a err, (a -> b) -> Task b err map = \effect, transform -> Effect.map effect - \result -> - when result is + \res -> + when res is Ok a -> Ok (transform a) Err err -> Err err @@ -91,7 +91,7 @@ result : Task ok err -> Task (Result ok err) * result = \effect -> Effect.after effect - \result -> Task.succeed result + \res -> Task.succeed res putLine : Str -> Task {} * putLine = \line -> Effect.map (Effect.putLine line) (\_ -> Ok {}) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 9904498c1e2..62f77a7a05b 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -23,6 +23,7 @@ mod cli_run { use serial_test::serial; use std::iter; use std::path::Path; + use std::process::ExitStatus; #[cfg(all(unix, not(target_os = "macos")))] const ALLOW_VALGRIND: bool = true; @@ -106,6 +107,11 @@ mod cli_run { assert_multiline_str_eq!(err.as_str(), expected); } + fn assert_valid_roc_check_status(status: ExitStatus) { + // 0 means no errors or warnings, 2 means just warnings + assert!(status.code().is_some_and(|code| code == 0 || code == 2)) + } + fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) { let out = run_roc([CMD_FORMAT, file.to_str().unwrap(), CHECK_FLAG], &[], &[]); @@ -803,7 +809,7 @@ mod cli_run { fn check_virtual_dom_server() { let path = file_path_from_root("examples/virtual-dom-wip", "example-server.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + assert_valid_roc_check_status(out.status); } // TODO: write a new test once mono bugs are resolved in investigation @@ -812,7 +818,7 @@ mod cli_run { fn check_virtual_dom_client() { let path = file_path_from_root("examples/virtual-dom-wip", "example-client.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + assert_valid_roc_check_status(out.status); } #[test] @@ -821,7 +827,7 @@ mod cli_run { fn cli_countdown_check() { let path = file_path_from_root("crates/cli/tests/cli", "countdown.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + assert_valid_roc_check_status(out.status); } #[test] @@ -830,7 +836,7 @@ mod cli_run { fn cli_echo_check() { let path = file_path_from_root("crates/cli/tests/cli", "echo.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + assert_valid_roc_check_status(out.status); } #[test] @@ -839,7 +845,7 @@ mod cli_run { fn cli_file_check() { let path = file_path_from_root("crates/cli/tests/cli", "fileBROKEN.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + assert_valid_roc_check_status(out.status); } #[test] @@ -848,7 +854,8 @@ mod cli_run { fn cli_form_check() { let path = file_path_from_root("crates/cli/tests/cli", "form.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + dbg!(out.stdout, out.stderr); + assert_valid_roc_check_status(out.status); } #[test] @@ -857,7 +864,7 @@ mod cli_run { fn cli_http_get_check() { let path = file_path_from_root("crates/cli/tests/cli", "http-get.roc"); let out = run_roc([CMD_CHECK, path.to_str().unwrap()], &[], &[]); - assert!(out.status.success()); + assert_valid_roc_check_status(out.status); } #[test] @@ -1482,9 +1489,9 @@ mod cli_run { Something is off with the body of the main definition: - 6│ main : Str -> Task {} [] - 7│ main = /_ -> - 8│ "this is a string, not a Task {} [] function like the platform expects." + 5│ main : Str -> Task {} [] + 6│ main = /_ -> + 7│ "this is a string, not a Task {} [] function like the platform expects." ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The body is a string of type: diff --git a/crates/cli/tests/known_bad/TypeError.roc b/crates/cli/tests/known_bad/TypeError.roc index 070aca541ae..d7509f9e85c 100644 --- a/crates/cli/tests/known_bad/TypeError.roc +++ b/crates/cli/tests/known_bad/TypeError.roc @@ -1,8 +1,7 @@ -app "type-error" - packages { pf: "../../../../examples/cli/false-interpreter/platform/main.roc" } - imports [pf.Task.{ Task }] - provides [main] to pf +app [main] { pf: platform "../../../../examples/cli/false-interpreter/platform/main.roc" } + +import pf.Task exposing [Task] main : Str -> Task {} [] main = \_ -> - "this is a string, not a Task {} [] function like the platform expects." \ No newline at end of file + "this is a string, not a Task {} [] function like the platform expects." diff --git a/examples/cli/false-interpreter/platform/Task.roc b/examples/cli/false-interpreter/platform/Task.roc index bdc608f2790..033ce78e41e 100644 --- a/examples/cli/false-interpreter/platform/Task.roc +++ b/examples/cli/false-interpreter/platform/Task.roc @@ -6,6 +6,7 @@ module [ map, onFail, attempt, + result, fromResult, loop, ] @@ -22,7 +23,7 @@ loop = \state, step -> \res -> when res is Ok (Step newState) -> Step newState - Ok (Done result) -> Done (Ok result) + Ok (Done res2) -> Done (Ok res2) Err e -> Done (Err e) Effect.loop state looper @@ -36,8 +37,8 @@ fail = \val -> Effect.always (Err val) fromResult : Result a e -> Task a e -fromResult = \result -> - when result is +fromResult = \res -> + when res is Ok a -> succeed a Err e -> fail e @@ -45,8 +46,8 @@ attempt : Task a b, (Result a b -> Task c d) -> Task c d attempt = \effect, transform -> Effect.after effect - \result -> - when result is + \res -> + when res is Ok ok -> transform (Ok ok) Err err -> transform (Err err) @@ -54,8 +55,8 @@ await : Task a err, (a -> Task b err) -> Task b err await = \effect, transform -> Effect.after effect - \result -> - when result is + \res -> + when res is Ok a -> transform a Err err -> Task.fail err @@ -63,8 +64,8 @@ onFail : Task ok a, (a -> Task ok b) -> Task ok b onFail = \effect, transform -> Effect.after effect - \result -> - when result is + \res -> + when res is Ok a -> Task.succeed a Err err -> transform err @@ -72,8 +73,8 @@ map : Task a err, (a -> b) -> Task b err map = \effect, transform -> Effect.after effect - \result -> - when result is + \res -> + when res is Ok a -> Task.succeed (transform a) Err err -> Task.fail err @@ -81,4 +82,4 @@ result : Task ok err -> Task (Result ok err) * result = \effect -> Effect.after effect - \result -> Task.succeed result + \res -> Task.succeed res From 7c90a3a9003eac88ca53a184eebab013bba57da6 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Fri, 16 Aug 2024 23:51:07 -0700 Subject: [PATCH 192/203] Remove vestigial backpassing test --- .../compiler/test_gen/src/gen_primitives.rs | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 60bdf275f82..fcbd4c66b96 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -2479,39 +2479,6 @@ fn expanded_result() { ); } -#[test] -#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -fn backpassing_result() { - assert_evals_to!( - indoc!( - r#" - app "test" provides [main] to "./platform" - - a : Result I64 Str - a = Ok 1 - - f = \x -> Ok (x + 1) - g = \y -> Ok (y * 2) - - main : I64 - main = - helper = - x <- Result.try a - y <- Result.try (f x) - z <- Result.try (g y) - - Ok z - - helper - |> Result.withDefault 0 - - "# - ), - 4, - i64 - ); -} - #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[should_panic(expected = "Shadowing { original_region: @55-56, shadow: @72-73 Ident")] From 1551d8fb0da07f2fb03e74ae2c2300b44dd02b75 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 17 Aug 2024 09:57:59 -0400 Subject: [PATCH 193/203] preserve multiline string trailing whitespace --- crates/compiler/fmt/src/expr.rs | 4 ++-- crates/compiler/test_syntax/tests/test_fmt.rs | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index f937a2c7e3d..97f5b3f45ab 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -627,8 +627,8 @@ fn fmt_str_body(body: &str, buf: &mut Buf) { '\u{200c}' => buf.push_str("\\u(200c)"), '\u{feff}' => buf.push_str("\\u(feff)"), // Don't change anything else in the string - ' ' => buf.spaces(1), - '\n' => buf.newline(), + ' ' => buf.push_str_allow_spaces(" "), + '\n' => buf.push_str_allow_spaces("\n"), _ => buf.push(c), } } diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 519bcbd5513..807825319f2 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6379,6 +6379,13 @@ mod test_fmt { ); } + #[test] + fn preserve_multiline_string_trailing_whitespace() { + expr_formats_same(indoc!( + "x =\n \"\"\"\n foo\n bar \n baz\n \"\"\"\nx" + )); + } + // this is a parse error atm // #[test] // fn multiline_apply() { From aef9d81eaa8b00074573e7d046f3ccffda0cc243 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 17 Aug 2024 10:06:50 -0400 Subject: [PATCH 194/203] simplify `StrLiteral::Line` to `StrLiteral::PlainLine` in block strings as well --- crates/compiler/parse/src/normalize.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 8aafe0f8307..53a24203631 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -642,6 +642,12 @@ impl<'a> Normalize<'a> for StrLiteral<'a> { new_segments.push(StrSegment::Plaintext(last_text.into_bump_str())); } + if new_segments.len() == 1 { + if let StrSegment::Plaintext(t) = new_segments[0] { + return StrLiteral::PlainLine(t); + } + } + StrLiteral::Line(new_segments.into_bump_slice()) } } From de6a31263a21a482d8d10fe48d44c7b12c93f22b Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 17 Aug 2024 10:23:22 -0400 Subject: [PATCH 195/203] DRY up my new `normalize` code --- crates/compiler/parse/src/normalize.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 53a24203631..2e11953c56d 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -624,13 +624,7 @@ impl<'a> Normalize<'a> for StrLiteral<'a> { new_segments.push(StrSegment::Plaintext(last_text.into_bump_str())); } - if new_segments.len() == 1 { - if let StrSegment::Plaintext(t) = new_segments[0] { - return StrLiteral::PlainLine(t); - } - } - - StrLiteral::Line(new_segments.into_bump_slice()) + normalize_str_line(new_segments) } StrLiteral::Block(t) => { let mut new_segments = Vec::new_in(arena); @@ -642,18 +636,22 @@ impl<'a> Normalize<'a> for StrLiteral<'a> { new_segments.push(StrSegment::Plaintext(last_text.into_bump_str())); } - if new_segments.len() == 1 { - if let StrSegment::Plaintext(t) = new_segments[0] { - return StrLiteral::PlainLine(t); - } - } - - StrLiteral::Line(new_segments.into_bump_slice()) + normalize_str_line(new_segments) } } } } +fn normalize_str_line<'a>(new_segments: Vec<'a, StrSegment<'a>>) -> StrLiteral<'a> { + if new_segments.len() == 1 { + if let StrSegment::Plaintext(t) = new_segments[0] { + return StrLiteral::PlainLine(t); + } + } + + StrLiteral::Line(new_segments.into_bump_slice()) +} + fn normalize_str_segments<'a>( arena: &'a Bump, segments: &[StrSegment<'a>], From ade416dbb4f50ed7955dacf72b76c0c18e6f7f26 Mon Sep 17 00:00:00 2001 From: Aidan Date: Sat, 17 Aug 2024 10:28:15 -0400 Subject: [PATCH 196/203] add test for blank chars in multiline string --- crates/compiler/test_syntax/tests/test_fmt.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 807825319f2..e0f9c8165e5 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -6379,6 +6379,30 @@ mod test_fmt { ); } + #[test] + fn make_blank_chars_explicit_in_multiline_string() { + expr_formats_to( + indoc!( + " + x = + \"\"\" + foo:\u{200B} $(bar). + \"\"\" + x + " + ), + indoc!( + r#" + x = + """ + foo:\u(200b) $(bar). + """ + x + "# + ), + ); + } + #[test] fn preserve_multiline_string_trailing_whitespace() { expr_formats_same(indoc!( From 5d69edfb97389d2ae82bd4e9681b4ce5574ec616 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:06:46 +0200 Subject: [PATCH 197/203] llvm still needs zstd The nightly won't build without this line Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- .github/workflows/nightly_macos_apple_silicon.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/nightly_macos_apple_silicon.yml b/.github/workflows/nightly_macos_apple_silicon.yml index 5e9cc69ca73..b4d12d700f4 100644 --- a/.github/workflows/nightly_macos_apple_silicon.yml +++ b/.github/workflows/nightly_macos_apple_silicon.yml @@ -14,6 +14,8 @@ jobs: test-and-build: name: Rust tests, build and package nightly release runs-on: [self-hosted, macOS, ARM64] + env: + LIBRARY_PATH: /opt/homebrew/Cellar/zstd/1.5.6/lib timeout-minutes: 90 steps: - uses: actions/checkout@v4 From 8f9d4149cab97bfd11473e041fefa5e48215bea0 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 20 Aug 2024 12:36:15 +0200 Subject: [PATCH 198/203] flush write_all Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- examples/platform-switching/rust-platform/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/platform-switching/rust-platform/src/lib.rs b/examples/platform-switching/rust-platform/src/lib.rs index f3c83ed5574..a9d1cc7b02a 100644 --- a/examples/platform-switching/rust-platform/src/lib.rs +++ b/examples/platform-switching/rust-platform/src/lib.rs @@ -93,6 +93,11 @@ pub extern "C" fn rust_main() -> i32 { panic!("Writing to stdout failed! {:?}", e); } + // roc_str will not print without flushing if it does not contain a newline and you're using --linker=legacy + if let Err(e) = std::io::stdout().flush() { + panic!("Failed to flush stdout: {:?}", e); + } + // Exit code 0 } From e458aba31f844e3e97376c5163cd66490aee0628 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 20 Aug 2024 20:31:48 +0200 Subject: [PATCH 199/203] Expanded debug_tips Signed-off-by: Anton-4 <17049058+Anton-4@users.noreply.github.com> --- devtools/debug_tips.md | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/devtools/debug_tips.md b/devtools/debug_tips.md index afd0918a336..324ddd00df3 100644 --- a/devtools/debug_tips.md +++ b/devtools/debug_tips.md @@ -12,6 +12,39 @@ - In general we recommend using linux to investigate, it has better tools for this. - If your segfault also happens when using `--linker=legacy`, use it to improve valgrind output. For example: `roc build myApp.roc --linker=legacy` followed by `valgrind ./myApp`. -- Use gdb to step through the code, [this gdb script](https://roc.zulipchat.com/#narrow/stream/395097-compiler-development/topic/gdb.20script/near/424422545) can be helpful. -- Use objdump to look at the assembly of the code, for example `objdump -d -M intel ./examples/Arithmetic/main`. Replace `-M intel` with the appropriate flag for your CPU. -- Inspect the generated LLVM IR (`roc build myApp.roc --emit-llvm-ir`) between Roc code that encounters the segfault and code that doesn't. \ No newline at end of file + +### Assembly debuggers + +Stepping through the executed assembly is super useful to find out what is going wrong. +Use a debugger (see below) and find the last executed instruction, look that instruction up and check its requirements. An instruction can for example require 16 bit alignment and passing it 8 byte aligned data can cause a segfault. +If you have a commit that works and one that doesn't, step through both executables at the same time to check where they differ. +It can also be useful to keep the llvm IR .ll files open on the side (`roc build myApp.roc --emit-llvm-ir`) to understand how that assembly was generated. +I like using both [IDA free](https://hex-rays.com/ida-free/) and gdb. +IDA free is easier to use and has nicer visualizations compared to gdb, but it does sometimes have difficulty with binaries created by surgical linking. +I've also [not been able to view output (stdout) of a program in IDA free](https://stackoverflow.com/questions/78888834/how-to-view-stdout-in-ida-debugger). + +objdump can also be used to look at the full assembly of the executable, for example `objdump -d -M intel ./examples/Arithmetic/main`. Replace `-M intel` with the appropriate flag for your CPU. +Note that the addresses shown in objdump may use a different offset compared to those in IDA or gdb. + +#### IDA free + +1. [Download here](https://hex-rays.com/ida-free/) +2. Build your roc app with the legacy linker if it does not error only with the surgical linker: `roc build myApp.roc --linker=legacy` +3. Open the produced executable with IDA free, don't change any of the suggested settings. +4. You probably want to go to the function you saw in valgrind like `List_walkTryHelp_...` [here](https://github.com/roc-lang/examples/pull/192#issuecomment-2269571439). You can use Ctrl+F in the Function s window in IDA free. +5. Right click and choose `Add Breakpoint` at the first instruction of the function you clicked on the previous step. +6. Run the debugger by pressing F9 +7. Use step into (F7) and step over (F8) to see what's going on. Keep an eye on the `General Registers` and `Stack view` windows while you're stepping. + + +#### gdb + +1. Set up [this handy gdb layout](https://github.com/cyrus-and/gdb-dashboard). +2. Start with `gdb ./your-roc-app-executable`, or if your executable takes command line arguments; `gdb --args ./your-roc-app-executable arg1 arg2` +3. Get the complete function name of the function you want to analyze: `info functions yourInterestingFunction` +4. Use that complete function name to set a breakpoint `break fullFunctionName` or set a breakpoint at a specific address `break*0x00000000012345` +5. Execute `run` to start debugging. +6. Step to the next assembly instruction with `si`, or use `ni` if you don't want to step into calls. + +gdb scripting is very useful, [for example](https://roc.zulipchat.com/#narrow/stream/395097-compiler-development/topic/gdb.20script/near/424422545). +ChatGPT and Claude are good at writing those scripts as well. From cf5cd84d605e1f663630c51bfd6f6f1b320fa35a Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 21 Aug 2024 09:04:07 -0700 Subject: [PATCH 200/203] fix alignment of 16 bytes --- crates/compiler/gen_llvm/src/llvm/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/gen_llvm/src/llvm/convert.rs b/crates/compiler/gen_llvm/src/llvm/convert.rs index cfd19b1c2f6..90ad7a56824 100644 --- a/crates/compiler/gen_llvm/src/llvm/convert.rs +++ b/crates/compiler/gen_llvm/src/llvm/convert.rs @@ -263,7 +263,7 @@ fn alignment_type(context: &Context, alignment: u32) -> BasicTypeEnum { 2 => context.i16_type().into(), 4 => context.i32_type().into(), 8 => context.i64_type().into(), - 16 => context.i128_type().into(), + 16 => context.f128_type().into(), _ => unimplemented!("weird alignment: {alignment}"), } } From 44b8abdeadc2793fccf5fb04d065bb358ee122a4 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 23 Aug 2024 15:50:05 +0200 Subject: [PATCH 201/203] update to basic-cli 0.14 --- crates/cli/src/format.rs | 4 ++-- crates/cli/tests/cli/combine-tasks.roc | 2 +- crates/cli/tests/cli/countdown.roc | 2 +- crates/cli/tests/cli/echo.roc | 2 +- crates/cli/tests/cli/env.roc | 2 +- crates/cli/tests/cli/fileBROKEN.roc | 2 +- crates/cli/tests/cli/form.roc | 2 +- crates/cli/tests/cli/http-get.roc | 2 +- crates/cli/tests/cli/ingested-file-bytes-no-ann.roc | 2 +- crates/cli/tests/cli/ingested-file-bytes.roc | 2 +- crates/cli/tests/cli/ingested-file.roc | 2 +- crates/cli/tests/cli/parse-args.roc | 2 +- crates/cli/tests/cli/parser-letter-counts.roc | 2 +- crates/cli/tests/cli/parser-movies-csv.roc | 2 +- crates/compiler/load_internal/tests/test_load.rs | 2 +- .../snapshots/pass/newline_in_packages.full.formatted.roc | 2 +- .../snapshots/pass/newline_in_packages.full.result-ast | 2 +- .../tests/snapshots/pass/newline_in_packages.full.roc | 2 +- examples/helloWorld.roc | 2 +- examples/inspect-logging.roc | 2 +- www/content/docs.md | 2 +- www/content/platforms.md | 2 +- www/content/tutorial.md | 8 ++++---- 23 files changed, 27 insertions(+), 27 deletions(-) diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index 6cd649a5ee2..46282a273e2 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -263,7 +263,7 @@ mod tests { use std::io::Write; use tempfile::{tempdir, TempDir}; - const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } + const FORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import pf.Task @@ -271,7 +271,7 @@ import pf.Task main = Stdout.line! "I'm a Roc application!""#; - const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } + const UNFORMATTED_ROC: &str = r#"app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout diff --git a/crates/cli/tests/cli/combine-tasks.roc b/crates/cli/tests/cli/combine-tasks.roc index 0e081c46447..2d3e46a3ea3 100644 --- a/crates/cli/tests/cli/combine-tasks.roc +++ b/crates/cli/tests/cli/combine-tasks.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import pf.Task exposing [Task] diff --git a/crates/cli/tests/cli/countdown.roc b/crates/cli/tests/cli/countdown.roc index 26b1ce386a2..cd4a5b90007 100644 --- a/crates/cli/tests/cli/countdown.roc +++ b/crates/cli/tests/cli/countdown.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdin import pf.Stdout diff --git a/crates/cli/tests/cli/echo.roc b/crates/cli/tests/cli/echo.roc index 5c77c7514df..f30f2df3e32 100644 --- a/crates/cli/tests/cli/echo.roc +++ b/crates/cli/tests/cli/echo.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdin import pf.Stdout diff --git a/crates/cli/tests/cli/env.roc b/crates/cli/tests/cli/env.roc index aeb2061571b..b99e774565f 100644 --- a/crates/cli/tests/cli/env.roc +++ b/crates/cli/tests/cli/env.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import pf.Stderr diff --git a/crates/cli/tests/cli/fileBROKEN.roc b/crates/cli/tests/cli/fileBROKEN.roc index 7a942d9a208..8d006cc3b0a 100644 --- a/crates/cli/tests/cli/fileBROKEN.roc +++ b/crates/cli/tests/cli/fileBROKEN.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import pf.Task exposing [Task] diff --git a/crates/cli/tests/cli/form.roc b/crates/cli/tests/cli/form.roc index 87b23d08d6a..11b020af688 100644 --- a/crates/cli/tests/cli/form.roc +++ b/crates/cli/tests/cli/form.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdin import pf.Stdout diff --git a/crates/cli/tests/cli/http-get.roc b/crates/cli/tests/cli/http-get.roc index 2ed69c5d200..615f327ed54 100644 --- a/crates/cli/tests/cli/http-get.roc +++ b/crates/cli/tests/cli/http-get.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Http import pf.Task exposing [Task] diff --git a/crates/cli/tests/cli/ingested-file-bytes-no-ann.roc b/crates/cli/tests/cli/ingested-file-bytes-no-ann.roc index e37e996edf9..f4b23a22a15 100644 --- a/crates/cli/tests/cli/ingested-file-bytes-no-ann.roc +++ b/crates/cli/tests/cli/ingested-file-bytes-no-ann.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import "test-file.txt" as testFile diff --git a/crates/cli/tests/cli/ingested-file-bytes.roc b/crates/cli/tests/cli/ingested-file-bytes.roc index 913f500a5a9..dbbdec680dc 100644 --- a/crates/cli/tests/cli/ingested-file-bytes.roc +++ b/crates/cli/tests/cli/ingested-file-bytes.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import "test-file.txt" as testFile : _ # the _ is optional diff --git a/crates/cli/tests/cli/ingested-file.roc b/crates/cli/tests/cli/ingested-file.roc index 577a7733f46..cd0b9471543 100644 --- a/crates/cli/tests/cli/ingested-file.roc +++ b/crates/cli/tests/cli/ingested-file.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import "ingested-file.roc" as ownCode : Str diff --git a/crates/cli/tests/cli/parse-args.roc b/crates/cli/tests/cli/parse-args.roc index 983d297ca55..a1ffe4c8684 100644 --- a/crates/cli/tests/cli/parse-args.roc +++ b/crates/cli/tests/cli/parse-args.roc @@ -1,5 +1,5 @@ app [main] { - pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", + pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br", } import pf.Stdout diff --git a/crates/cli/tests/cli/parser-letter-counts.roc b/crates/cli/tests/cli/parser-letter-counts.roc index 4e1a749cf8c..aa40b5d3585 100644 --- a/crates/cli/tests/cli/parser-letter-counts.roc +++ b/crates/cli/tests/cli/parser-letter-counts.roc @@ -1,5 +1,5 @@ app [main] { - cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", + cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br", parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br", } diff --git a/crates/cli/tests/cli/parser-movies-csv.roc b/crates/cli/tests/cli/parser-movies-csv.roc index eed79b17544..ffd7f4e5da5 100644 --- a/crates/cli/tests/cli/parser-movies-csv.roc +++ b/crates/cli/tests/cli/parser-movies-csv.roc @@ -1,5 +1,5 @@ app [main] { - pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", + pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br", parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.5.2/9VrPjwfQQ1QeSL3CfmWr2Pr9DESdDIXy97pwpuq84Ck.tar.br", } diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index ca4363b8406..30d530635eb 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -2151,7 +2151,7 @@ fn roc_file_no_extension() { indoc!( r#" app "helloWorld" - packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } + packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } imports [pf.Stdout] provides [main] to pf diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.formatted.roc index cde1414e952..79d637033ee 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.formatted.roc @@ -1,6 +1,6 @@ app [main] { pf: - "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", + "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br", } main = diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast index 7e12303321b..3f702b5ba16 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast @@ -20,7 +20,7 @@ Full( ], platform_marker: None, package_name: @17-132 PackageName( - "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br", + "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br", ), }, [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.roc index 57ad5cb342b..47dba0ce189 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.roc @@ -1,5 +1,5 @@ app [main] { pf: -"https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" +"https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } main = diff --git a/examples/helloWorld.roc b/examples/helloWorld.roc index 75be28443aa..a53b85353e0 100644 --- a/examples/helloWorld.roc +++ b/examples/helloWorld.roc @@ -1,4 +1,4 @@ -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import pf.Task diff --git a/examples/inspect-logging.roc b/examples/inspect-logging.roc index a5b8100a6fd..8e4d81c1bf3 100644 --- a/examples/inspect-logging.roc +++ b/examples/inspect-logging.roc @@ -1,7 +1,7 @@ # # Shows how Roc values can be logged # -app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" } +app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.14.0/dC5ceT962N_4jmoyoffVdphJ_4GlW3YMhAPyGPr-nU0.tar.br" } import pf.Stdout import pf.Task diff --git a/www/content/docs.md b/www/content/docs.md index 1407c541d44..2766fb47809 100644 --- a/www/content/docs.md +++ b/www/content/docs.md @@ -2,7 +2,7 @@ - [builtins](/builtins) - docs for modules built into the language—`Str`, `Num`, etc. - [basic-webserver](https://roc-lang.github.io/basic-webserver/) - a platform for making Web servers ([source code](https://github.com/roc-lang/basic-webserver)) -- [basic-cli](/packages/basic-cli/0.12.0) - a platform for making command-line interfaces ([source code](https://github.com/roc-lang/basic-cli)) +- [basic-cli](/packages/basic-cli/0.14.0) - a platform for making command-line interfaces ([source code](https://github.com/roc-lang/basic-cli)) - [plans](/plans) - current plans for future changes to the language In the future, a language reference will be on this page too. diff --git a/www/content/platforms.md b/www/content/platforms.md index 970a180989b..23b7ffadb6d 100644 --- a/www/content/platforms.md +++ b/www/content/platforms.md @@ -7,7 +7,7 @@ Something that sets Roc apart from other programming languages is its Date: Fri, 23 Aug 2024 19:16:16 +0200 Subject: [PATCH 202/203] update docker tests --- .github/workflows/ci_manager.yml | 2 +- .github/workflows/docker.yml | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci_manager.yml b/.github/workflows/ci_manager.yml index f0548533f33..d20329bd3c6 100644 --- a/.github/workflows/ci_manager.yml +++ b/.github/workflows/ci_manager.yml @@ -1,5 +1,5 @@ on: - pull_request: +# pull_request: name: CI manager diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 833c7bb353a..f935beacd59 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,5 +1,7 @@ on: workflow_dispatch: + pull_request: +# TODO remove pull_request trigger name: Docker images tests @@ -15,10 +17,10 @@ jobs: run: cp docker/nightly-ubuntu-latest/docker-compose.example.yml docker/nightly-ubuntu-latest/docker-compose.yml - name: Build image - run: docker-compose -f docker/nightly-ubuntu-latest/docker-compose.yml build + run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml build - name: Run hello world test - run: docker-compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc + run: docker compose -f docker/nightly-ubuntu-latest/docker-compose.yml run roc examples/helloWorld.roc nightly-ubuntu-2204: @@ -32,10 +34,10 @@ jobs: run: cp docker/nightly-ubuntu-2204/docker-compose.example.yml docker/nightly-ubuntu-2204/docker-compose.yml - name: Build image - run: docker-compose -f docker/nightly-ubuntu-2204/docker-compose.yml build + run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml build - name: Run hello world test - run: docker-compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc + run: docker compose -f docker/nightly-ubuntu-2204/docker-compose.yml run roc examples/helloWorld.roc nightly-ubuntu-2004: name: nightly-ubuntu-2004 @@ -48,10 +50,10 @@ jobs: run: cp docker/nightly-ubuntu-2004/docker-compose.example.yml docker/nightly-ubuntu-2004/docker-compose.yml - name: Build image - run: docker-compose -f docker/nightly-ubuntu-2004/docker-compose.yml build + run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml build - name: Run hello world test - run: docker-compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc + run: docker compose -f docker/nightly-ubuntu-2004/docker-compose.yml run roc examples/helloWorld.roc nightly-debian-latest: name: nightly-debian-latest @@ -64,10 +66,10 @@ jobs: run: cp docker/nightly-debian-latest/docker-compose.example.yml docker/nightly-debian-latest/docker-compose.yml - name: Build image - run: docker-compose -f docker/nightly-debian-latest/docker-compose.yml build + run: docker compose -f docker/nightly-debian-latest/docker-compose.yml build - name: Run hello world test - run: docker-compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc + run: docker compose -f docker/nightly-debian-latest/docker-compose.yml run roc examples/helloWorld.roc nightly-debian-bookworm: name: nightly-debian-bookworm @@ -80,10 +82,10 @@ jobs: run: cp docker/nightly-debian-bookworm/docker-compose.example.yml docker/nightly-debian-bookworm/docker-compose.yml - name: Build image - run: docker-compose -f docker/nightly-debian-bookworm/docker-compose.yml build + run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml build - name: Run hello world test - run: docker-compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc + run: docker compose -f docker/nightly-debian-bookworm/docker-compose.yml run roc examples/helloWorld.roc nightly-debian-buster: name: nightly-debian-buster @@ -96,7 +98,7 @@ jobs: run: cp docker/nightly-debian-buster/docker-compose.example.yml docker/nightly-debian-buster/docker-compose.yml - name: Build image - run: docker-compose -f docker/nightly-debian-buster/docker-compose.yml build + run: docker compose -f docker/nightly-debian-buster/docker-compose.yml build - name: Run hello world test - run: docker-compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc + run: docker compose -f docker/nightly-debian-buster/docker-compose.yml run roc examples/helloWorld.roc From e35e564aa6f9e287714d36cbc912a9afcc69f13b Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 23 Aug 2024 19:17:17 +0200 Subject: [PATCH 203/203] revert --- .github/workflows/ci_manager.yml | 2 +- .github/workflows/docker.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci_manager.yml b/.github/workflows/ci_manager.yml index d20329bd3c6..f0548533f33 100644 --- a/.github/workflows/ci_manager.yml +++ b/.github/workflows/ci_manager.yml @@ -1,5 +1,5 @@ on: -# pull_request: + pull_request: name: CI manager diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f935beacd59..8f7197b641b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,6 +1,6 @@ on: workflow_dispatch: - pull_request: +# pull_request: # TODO remove pull_request trigger name: Docker images tests