diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 16cc4502c6..56adc9eb27 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -16,7 +16,6 @@ use roc_types::types::{ gather_fields_unsorted_iter, AliasKind, Category, ErrorType, PatternCategory, }; use roc_unify::unify::{unify, Mode, Unified::*}; -use std::collections::hash_map::Entry; // Type checking system adapted from Elm by Evan Czaplicki, BSD-3-Clause Licensed // https://github.com/elm/compiler @@ -915,6 +914,25 @@ impl RegisterVariable { _ => Deferred, } } + + #[inline(always)] + fn with_stack<'a>( + subs: &mut Subs, + rank: Rank, + pools: &mut Pools, + arena: &'_ bumpalo::Bump, + typ: &'a Type, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, + ) -> Variable { + match Self::from_type(subs, rank, pools, arena, typ) { + Self::Direct(var) => var, + Self::Deferred => { + let var = subs.fresh_unnamed_flex_var(); + stack.push(TypeToVar::Defer(typ, var)); + var + } + } + } } #[derive(Debug)] @@ -1046,7 +1064,8 @@ fn type_to_variable<'a>( // If hit, try to turn the value into an EmptyTagUnion in canonicalization debug_assert!(!tags.is_empty() || !ext.is_empty_tag_union()); - let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext); + let (union_tags, ext) = + type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack); let content = Content::Structure(FlatType::TagUnion(union_tags, ext)); register_with_known_var(subs, destination, rank, pools, content) @@ -1076,7 +1095,8 @@ fn type_to_variable<'a>( // If hit, try to turn the value into an EmptyTagUnion in canonicalization debug_assert!(!tags.is_empty() || !ext.is_empty_tag_union()); - let (union_tags, ext) = type_to_union_tags(subs, rank, pools, arena, tags, ext); + let (union_tags, ext) = + type_to_union_tags(subs, rank, pools, arena, tags, ext, &mut stack); let content = Content::Structure(FlatType::RecursiveTagUnion(*rec_var, union_tags, ext)); @@ -1132,7 +1152,7 @@ fn type_to_variable<'a>( }; let alias_variable = if let Symbol::RESULT_RESULT = *symbol { - roc_result_to_var(subs, rank, pools, arena, actual) + roc_result_to_var(subs, rank, pools, arena, actual, &mut stack) } else { helper!(actual) }; @@ -1212,8 +1232,9 @@ fn roc_result_to_var<'a>( subs: &mut Subs, rank: Rank, pools: &mut Pools, - arena: &'a bumpalo::Bump, - result_type: &Type, + arena: &'_ bumpalo::Bump, + result_type: &'a Type, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, ) -> Variable { match result_type { Type::TagUnion(tags, ext) => { @@ -1225,8 +1246,10 @@ fn roc_result_to_var<'a>( debug_assert_eq!(ok, &subs.tag_names[1]); if let ([err_type], [ok_type]) = (err_args.as_slice(), ok_args.as_slice()) { - let err_var = type_to_variable(subs, rank, pools, arena, err_type); - let ok_var = type_to_variable(subs, rank, pools, arena, ok_type); + let err_var = + RegisterVariable::with_stack(subs, rank, pools, arena, err_type, stack); + let ok_var = + RegisterVariable::with_stack(subs, rank, pools, arena, ok_type, stack); let start = subs.variables.len() as u32; let err_slice = SubsSlice::new(start, 1); @@ -1317,16 +1340,16 @@ fn sort_and_deduplicate(tag_vars: &mut bumpalo::collections::Vec<(TagName, T) fn find_tag_name_run(slice: &[(TagName, T)], subs: &mut Subs) -> Option> { use std::cmp::Ordering; - let tag_name = slice.get(0)?.0.clone(); + let tag_name = &slice.get(0)?.0; let mut result = None; // the `SubsSlice` that inserting `slice` into subs would give let bigger_slice = SubsSlice::new(subs.tag_names.len() as _, slice.len() as _); - match subs.tag_name_cache.entry(tag_name) { - Entry::Occupied(mut occupied) => { - let subs_slice = *occupied.get(); + match subs.tag_name_cache.get_mut(tag_name) { + Some(occupied) => { + let subs_slice = *occupied; let prefix_slice = SubsSlice::new(subs_slice.start, slice.len() as _); @@ -1360,12 +1383,12 @@ fn find_tag_name_run(slice: &[(TagName, T)], subs: &mut Subs) -> Option { // switch to the bigger slice that is not inserted yet, but will be soon - occupied.insert(bigger_slice); + *occupied = bigger_slice; } } } - Entry::Vacant(vacant) => { - vacant.insert(bigger_slice); + None => { + subs.tag_name_cache.push(tag_name, bigger_slice); } } @@ -1377,8 +1400,9 @@ fn insert_tags_fast_path<'a>( subs: &mut Subs, rank: Rank, pools: &mut Pools, - arena: &'a bumpalo::Bump, - tags: &[(TagName, Vec)], + arena: &'_ bumpalo::Bump, + tags: &'a [(TagName, Vec)], + stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, ) -> UnionTags { let new_variable_slices = SubsSlice::reserve_variable_slices(subs, tags.len()); @@ -1391,7 +1415,8 @@ fn insert_tags_fast_path<'a>( let new_variables = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); let it = (new_variables.indices()).zip(arguments); for (target_index, argument) in it { - let var = type_to_variable(subs, rank, pools, arena, argument); + let var = + RegisterVariable::with_stack(subs, rank, pools, arena, argument, stack); subs.variables[target_index] = var; } @@ -1412,7 +1437,8 @@ fn insert_tags_fast_path<'a>( let new_variables = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); let it = (new_variables.indices()).zip(arguments); for (target_index, argument) in it { - let var = type_to_variable(subs, rank, pools, arena, argument); + let var = + RegisterVariable::with_stack(subs, rank, pools, arena, argument, stack); subs.variables[target_index] = var; } @@ -1429,15 +1455,16 @@ fn insert_tags_slow_path<'a>( subs: &mut Subs, rank: Rank, pools: &mut Pools, - arena: &'a bumpalo::Bump, - tags: &[(TagName, Vec)], + arena: &'_ bumpalo::Bump, + tags: &'a [(TagName, Vec)], mut tag_vars: bumpalo::collections::Vec<(TagName, VariableSubsSlice)>, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, ) -> UnionTags { for (tag, tag_argument_types) in tags { let new_slice = VariableSubsSlice::reserve_into_subs(subs, tag_argument_types.len()); for (i, arg) in (new_slice.indices()).zip(tag_argument_types) { - let var = type_to_variable(subs, rank, pools, arena, arg); + let var = RegisterVariable::with_stack(subs, rank, pools, arena, arg, stack); subs.variables[i] = var; } @@ -1453,39 +1480,39 @@ fn type_to_union_tags<'a>( subs: &mut Subs, rank: Rank, pools: &mut Pools, - arena: &'a bumpalo::Bump, - tags: &[(TagName, Vec)], - ext: &Type, + arena: &'_ bumpalo::Bump, + tags: &'a [(TagName, Vec)], + ext: &'a Type, + stack: &mut bumpalo::collections::Vec<'_, TypeToVar<'a>>, ) -> (UnionTags, Variable) { use bumpalo::collections::Vec; let sorted = tags.len() == 1 || sorted_no_duplicates(tags); if ext.is_empty_tag_union() { - let ext = type_to_variable(subs, rank, pools, arena, &Type::EmptyTagUnion); - // let ext = Variable::EMPTY_TAG_UNION; + let ext = Variable::EMPTY_TAG_UNION; let union_tags = if sorted { - insert_tags_fast_path(subs, rank, pools, arena, tags) + insert_tags_fast_path(subs, rank, pools, arena, tags, stack) } else { let tag_vars = Vec::with_capacity_in(tags.len(), arena); - insert_tags_slow_path(subs, rank, pools, arena, tags, tag_vars) + insert_tags_slow_path(subs, rank, pools, arena, tags, tag_vars, stack) }; (union_tags, ext) } else { let mut tag_vars = Vec::with_capacity_in(tags.len(), arena); - let temp_ext_var = type_to_variable(subs, rank, pools, arena, ext); + let temp_ext_var = RegisterVariable::with_stack(subs, rank, pools, arena, ext, stack); let (it, ext) = roc_types::types::gather_tags_unsorted_iter(subs, UnionTags::default(), temp_ext_var); tag_vars.extend(it.map(|(n, v)| (n.clone(), v))); let union_tags = if tag_vars.is_empty() && sorted { - insert_tags_fast_path(subs, rank, pools, arena, tags) + insert_tags_fast_path(subs, rank, pools, arena, tags, stack) } else { - insert_tags_slow_path(subs, rank, pools, arena, tags, tag_vars) + insert_tags_slow_path(subs, rank, pools, arena, tags, tag_vars, stack) }; (union_tags, ext) diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index 164367a28e..2324d5a8f9 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -1,5 +1,5 @@ use crate::types::{name_type_var, AliasKind, ErrorType, Problem, RecordField, TypeExt}; -use roc_collections::all::{ImMap, ImSet, MutMap, MutSet, SendMap}; +use roc_collections::all::{ImMap, ImSet, MutSet, SendMap}; use roc_module::ident::{Lowercase, TagName, Uppercase}; use roc_module::symbol::Symbol; use std::fmt; @@ -68,7 +68,49 @@ pub struct Subs { pub field_names: Vec, pub record_fields: Vec>, pub variable_slices: Vec, - pub tag_name_cache: MutMap>, + pub tag_name_cache: TagNameCache, +} + +#[derive(Debug, Clone, Default)] +pub struct TagNameCache { + globals: Vec, + globals_slices: Vec>, + /// Currently private tags and closure tags; in the future just closure tags + symbols: Vec, + symbols_slices: Vec>, +} + +impl TagNameCache { + pub fn get_mut(&mut self, tag_name: &TagName) -> Option<&mut SubsSlice> { + match tag_name { + TagName::Global(uppercase) => { + // force into block + match self.globals.iter().position(|u| u == uppercase) { + Some(index) => Some(&mut self.globals_slices[index]), + None => None, + } + } + TagName::Private(symbol) | TagName::Closure(symbol) => { + match self.symbols.iter().position(|s| s == symbol) { + Some(index) => Some(&mut self.symbols_slices[index]), + None => None, + } + } + } + } + + pub fn push(&mut self, tag_name: &TagName, slice: SubsSlice) { + match tag_name { + TagName::Global(uppercase) => { + self.globals.push(uppercase.clone()); + self.globals_slices.push(slice); + } + TagName::Private(symbol) | TagName::Closure(symbol) => { + self.symbols.push(*symbol); + self.symbols_slices.push(slice); + } + } + } } impl Default for Subs { @@ -1248,7 +1290,7 @@ impl Subs { // store an empty slice at the first position // used for "TagOrFunction" variable_slices: vec![VariableSubsSlice::default()], - tag_name_cache: MutMap::default(), + tag_name_cache: TagNameCache::default(), }; // NOTE the utable does not (currently) have a with_capacity; using this as the next-best thing diff --git a/repl_www/public/repl.css b/repl_www/public/repl.css index f39bccbff6..2c39fc8a3d 100644 --- a/repl_www/public/repl.css +++ b/repl_www/public/repl.css @@ -43,16 +43,20 @@ section.history { margin: 16px 0; padding: 8px; } -#history-text .input { +.history-item { + margin-bottom: 24px; +} +.history-item .input { + margin: 0; margin-bottom: 8px; } -#history-text .output { - margin-bottom: 16px; +.history-item .output { + margin: 0; } -#history-text .output-ok { +.history-item .output-ok { color: #0f8; } -#history-text .output-error { +.history-item .output-error { color: #f00; } .code { @@ -64,9 +68,7 @@ section.source { display: flex; flex-direction: column; } - section.source textarea { - height: 32px; padding: 8px; margin-bottom: 16px; } diff --git a/repl_www/public/repl.js b/repl_www/public/repl.js index 008a08c8e7..4ad0323e15 100644 --- a/repl_www/public/repl.js +++ b/repl_www/public/repl.js @@ -82,7 +82,9 @@ function onInputKeyup(event) { break; case ENTER: - onInputChange({ target: repl.elemSourceInput }); + if (!event.shiftKey) { + onInputChange({ target: repl.elemSourceInput }); + } break; default: @@ -168,12 +170,13 @@ function createHistoryEntry(inputText) { const historyIndex = repl.inputHistory.length; repl.inputHistory.push(inputText); - const inputElem = document.createElement("div"); - inputElem.textContent = "> " + inputText; + const inputElem = document.createElement("pre"); + inputElem.textContent = inputText; inputElem.classList.add("input"); const historyItem = document.createElement("div"); historyItem.appendChild(inputElem); + historyItem.classList.add("history-item"); repl.elemHistory.appendChild(historyItem); repl.elemHistory.scrollTop = repl.elemHistory.scrollHeight; @@ -182,7 +185,7 @@ function createHistoryEntry(inputText) { } function updateHistoryEntry(index, ok, outputText) { - const outputElem = document.createElement("div"); + const outputElem = document.createElement("pre"); outputElem.textContent = outputText; outputElem.classList.add("output"); outputElem.classList.add(ok ? "output-ok" : "output-error"); diff --git a/repl_www/public/repl/index.html b/repl_www/public/repl/index.html index 5bc4ac7e76..4c71ebc6ed 100644 --- a/repl_www/public/repl/index.html +++ b/repl_www/public/repl/index.html @@ -1,29 +1,33 @@ + + Roc REPL + + - - Roc REPL - - + +
+
+

The rockin' Roc REPL

+
- -
-
-

The rockin' Roc REPL

-
- -
-
-
-
-
- -
- -
-
- - +
+
+
+
+
+
+ +
+
+ + diff --git a/www/build.sh b/www/build.sh index ae07359997..86db1d0134 100755 --- a/www/build.sh +++ b/www/build.sh @@ -12,13 +12,18 @@ rm -rf build/ cp -r public/ build/ pushd build + # grab the source code and copy it to Netlify's server; if it's not there, fail the build. wget https://github.com/rtfeldman/elm-css/files/8037422/roc-source-code.zip # grab the pre-compiled REPL and copy it to Netlify's server; if it's not there, fail the build. -wget https://github.com/brian-carroll/mock-repl/files/8167902/roc_repl_wasm.tar.gz -tar xzvf roc_repl_wasm.tar.gz -rm roc_repl_wasm.tar.gz +wget https://github.com/brian-carroll/mock-repl/archive/refs/heads/deploy.zip +unzip deploy.zip +mv mock-repl-deploy/* . +rmdir mock-repl-deploy +rm deploy.zip + +# Copy REPL webpage source files cp -r ../../repl_www/public/* . popd