From 82bb341e9ca8687736c103d39739c9fa6319ed31 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 1 Dec 2021 19:26:01 +0100 Subject: [PATCH 001/132] add constraint soa form --- Cargo.lock | 1 + compiler/can/Cargo.toml | 1 + compiler/can/src/constraint_soa.rs | 194 +++++++++++++++++++++++++++++ compiler/can/src/lib.rs | 1 + 4 files changed, 197 insertions(+) create mode 100644 compiler/can/src/constraint_soa.rs diff --git a/Cargo.lock b/Cargo.lock index 3cd24a5c1f..ee75ebfb7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3208,6 +3208,7 @@ dependencies = [ "roc_problem", "roc_region", "roc_types", + "static_assertions", "ven_graph", ] diff --git a/compiler/can/Cargo.toml b/compiler/can/Cargo.toml index 2e8bd639dd..edec9d23ff 100644 --- a/compiler/can/Cargo.toml +++ b/compiler/can/Cargo.toml @@ -15,6 +15,7 @@ roc_types = { path = "../types" } roc_builtins = { path = "../builtins" } ven_graph = { path = "../../vendor/pathfinding" } bumpalo = { version = "3.8.0", features = ["collections"] } +static_assertions = "1.1.0" [dev-dependencies] pretty_assertions = "1.0.0" diff --git a/compiler/can/src/constraint_soa.rs b/compiler/can/src/constraint_soa.rs new file mode 100644 index 0000000000..a25c630755 --- /dev/null +++ b/compiler/can/src/constraint_soa.rs @@ -0,0 +1,194 @@ +use crate::expected::{Expected, PExpected}; +use roc_collections::all::{MutSet, SendMap}; +use roc_module::symbol::Symbol; +use roc_region::all::{Located, Region}; +use roc_types::types::{Category, PatternCategory, Type}; +use roc_types::{subs::Variable, types::VariableDetail}; + +pub struct Constraints { + constraints: Vec, + types: Vec, + variables: Vec, + def_types: Vec<(Symbol, Located>)>, + let_constraints: Vec, + categories: Vec, + pattern_categories: Vec, + expectations: Vec>, + pattern_expectations: Vec>, +} + +impl Constraints { + pub fn equal_types( + &mut self, + typ: Type, + expected: Expected, + category: Category, + region: Region, + ) -> Constraint { + let type_index = Index::new(self.types.len() as _); + let expected_index = Index::new(self.expectations.len() as _); + let category_index = Index::new(self.categories.len() as _); + + self.types.push(typ); + self.expectations.push(expected); + self.categories.push(category); + + Constraint::Eq(type_index, expected_index, category_index, region) + } + + pub fn equal_pattern_types( + &mut self, + typ: Type, + expected: PExpected, + category: PatternCategory, + region: Region, + ) -> Constraint { + let type_index = Index::new(self.types.len() as _); + let expected_index = Index::new(self.pattern_expectations.len() as _); + let category_index = Index::new(self.pattern_categories.len() as _); + + self.types.push(typ); + self.pattern_expectations.push(expected); + self.pattern_categories.push(category); + + Constraint::Pattern(type_index, expected_index, category_index, region) + } + + fn variable_slice(&mut self, it: I) -> Slice + where + I: IntoIterator, + { + let start = self.variables.len(); + self.variables.extend(it); + let length = self.variables.len() - start; + + Slice::new(start as _, length as _) + } + + fn def_types_slice(&mut self, it: I) -> Slice<(Symbol, Located)> + where + I: IntoIterator)>, + { + let start = self.def_types.len(); + + for (symbol, loc_type) in it { + let type_index = Index::new(self.types.len() as _); + let Located { region, value } = loc_type; + self.types.push(value); + + self.def_types + .push((symbol, Located::at(region, type_index))); + } + + let length = self.def_types.len() - start; + + Slice::new(start as _, length as _) + } + + pub fn let_contraint( + &mut self, + rigid_vars: I1, + flex_vars: I2, + def_types: I3, + defs_constraint: Constraint, + ret_constraint: Constraint, + ) -> Constraint + where + I1: IntoIterator, + I2: IntoIterator, + I3: IntoIterator)>, + { + let defs_and_ret_constraint = Index::new(self.constraints.len() as _); + + self.constraints.push(defs_constraint); + self.constraints.push(ret_constraint); + + let let_contraint = LetConstraint { + rigid_vars: self.variable_slice(rigid_vars), + flex_vars: self.variable_slice(flex_vars), + def_types: self.def_types_slice(def_types), + defs_and_ret_constraint, + }; + + let let_index = Index::new(self.let_constraints.len() as _); + self.let_constraints.push(let_contraint); + + Constraint::Let(let_index) + } +} + +static_assertions::assert_eq_size!([u8; 4 * 8], Constraint); +static_assertions::assert_eq_size!([u8; 3 * 8 + 4], LetConstraint); + +#[derive(Debug, Clone, PartialEq)] +pub enum Constraint { + Eq(Index, Index>, Index, Region), + Store(Index, Variable, &'static str, u32), + Lookup(Symbol, Index>, Region), + Pattern( + Index, + Index>, + Index, + Region, + ), + True, // Used for things that always unify, e.g. blanks and runtime errors + SaveTheEnvironment, + Let(Index), + And(Slice), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct LetConstraint { + pub rigid_vars: Slice, + pub flex_vars: Slice, + pub def_types: Slice<(Symbol, Located)>, + pub defs_and_ret_constraint: Index<(Constraint, Constraint)>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Index { + index: u32, + _marker: std::marker::PhantomData, +} + +impl Index { + pub const fn new(index: u32) -> Self { + Self { + index, + _marker: std::marker::PhantomData, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Slice { + start: u32, + length: u16, + _marker: std::marker::PhantomData, +} + +impl Slice { + pub const fn new(start: u32, length: u16) -> Self { + Self { + start, + length, + _marker: std::marker::PhantomData, + } + } + + pub const fn len(&self) -> usize { + self.length as _ + } + + pub const fn is_empty(&self) -> bool { + self.length == 0 + } + + pub const fn indices(&self) -> std::ops::Range { + self.start as usize..(self.start as usize + self.length as usize) + } + + pub fn into_iter(&self) -> impl Iterator> { + self.indices().map(|i| Index::new(i as _)) + } +} diff --git a/compiler/can/src/lib.rs b/compiler/can/src/lib.rs index 7230ab1a36..5cd16be75a 100644 --- a/compiler/can/src/lib.rs +++ b/compiler/can/src/lib.rs @@ -4,6 +4,7 @@ pub mod annotation; pub mod builtins; pub mod constraint; +pub mod constraint_soa; pub mod def; pub mod env; pub mod expected; From a23b76d35f922e4982de4d20d6ecd0462edca045 Mon Sep 17 00:00:00 2001 From: Folkert Date: Wed, 1 Dec 2021 20:01:48 +0100 Subject: [PATCH 002/132] more helpers --- compiler/can/src/constraint_soa.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/compiler/can/src/constraint_soa.rs b/compiler/can/src/constraint_soa.rs index a25c630755..05e1652340 100644 --- a/compiler/can/src/constraint_soa.rs +++ b/compiler/can/src/constraint_soa.rs @@ -85,6 +85,28 @@ impl Constraints { Slice::new(start as _, length as _) } + pub fn exists(&mut self, flex_vars: I, defs_constraint: Constraint) -> Constraint + where + I: IntoIterator, + { + let defs_and_ret_constraint = Index::new(self.constraints.len() as _); + + self.constraints.push(defs_constraint); + self.constraints.push(Constraint::True); + + let let_contraint = LetConstraint { + rigid_vars: Slice::default(), + flex_vars: self.variable_slice(flex_vars), + def_types: Slice::default(), + defs_and_ret_constraint, + }; + + let let_index = Index::new(self.let_constraints.len() as _); + self.let_constraints.push(let_contraint); + + Constraint::Let(let_index) + } + pub fn let_contraint( &mut self, rigid_vars: I1, @@ -167,6 +189,12 @@ pub struct Slice { _marker: std::marker::PhantomData, } +impl Default for Slice { + fn default() -> Self { + Self::new(0, 0) + } +} + impl Slice { pub const fn new(start: u32, length: u16) -> Self { Self { From ba2e8cd32b0379e16efeebd6c8be1e5fd83ad70f Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 17:58:56 -0800 Subject: [PATCH 003/132] Add base piping for list.Replace --- compiler/builtins/bitcode/README.md | 2 +- compiler/builtins/bitcode/src/list.zig | 87 ++++++++++++++++++++++ compiler/builtins/bitcode/src/main.zig | 2 + compiler/builtins/src/bitcode.rs | 2 + compiler/builtins/src/std.rs | 18 ++++- compiler/can/src/builtins.rs | 94 +++++++++++++++++++++++- compiler/gen_llvm/src/llvm/build.rs | 19 ++++- compiler/gen_llvm/src/llvm/build_list.rs | 44 +++++++++++ compiler/gen_wasm/src/low_level.rs | 16 ++-- compiler/module/src/low_level.rs | 2 + compiler/module/src/symbol.rs | 1 + compiler/mono/src/borrow.rs | 1 + compiler/test_gen/src/gen_list.rs | 34 +++++++++ compiler/types/src/builtin_aliases.rs | 9 +++ 14 files changed, 315 insertions(+), 16 deletions(-) diff --git a/compiler/builtins/bitcode/README.md b/compiler/builtins/bitcode/README.md index 604f3a53d0..1585c2e8fb 100644 --- a/compiler/builtins/bitcode/README.md +++ b/compiler/builtins/bitcode/README.md @@ -7,7 +7,7 @@ To add a builtin: 2. Make sure the function is public with the `pub` keyword and uses the C calling convention. This is really easy, just add `pub` and `callconv(.C)` to the function declaration like so: `pub fn atan(num: f64) callconv(.C) f64 { ... }` 3. In `src/main.zig`, export the function. This is also organized by module. For example, for a `Num` function find the `Num` section and add: `comptime { exportNumFn(num.atan, "atan"); }`. The first argument is the function, the second is the name of it in LLVM. 4. In `compiler/builtins/src/bitcode.rs`, add a constant for the new function. This is how we use it in Rust. Once again, this is organized by module, so just find the relevant area and add your new function. -5. You can now your function in Rust using `call_bitcode_fn` in `llvm/src/build.rs`! +5. You can now use your function in Rust using `call_bitcode_fn` in `llvm/src/build.rs`! ## How it works diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 70925554f1..012f24b141 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -1256,6 +1256,93 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt return output; } +pub fn listReplaceInPlace( + bytes: ?[*]u8, + index: usize, + element: Opaque, + element_width: usize, +) callconv(.C) ?[*]u8 { + // INVARIANT: bounds checking happens on the roc side + // + // at the time of writing, the function is implemented roughly as + // `if inBounds then LowLevelListReplace input index item else input` + // so we don't do a bounds check here. Hence, the list is also non-empty, + // because inserting into an empty list is always out of bounds + + return listReplaceInPlaceHelp(bytes, index, element, element_width); +} + +pub fn listReplace( + bytes: ?[*]u8, + length: usize, + alignment: u32, + index: usize, + element: Opaque, + element_width: usize, +) callconv(.C) ?[*]u8 { + // INVARIANT: bounds checking happens on the roc side + // + // at the time of writing, the function is implemented roughly as + // `if inBounds then LowLevelListReplace input index item else input` + // so we don't do a bounds check here. Hence, the list is also non-empty, + // because inserting into an empty list is always out of bounds + const ptr: [*]usize = @ptrCast([*]usize, @alignCast(@alignOf(usize), bytes)); + + if ((ptr - 1)[0] == utils.REFCOUNT_ONE) { + return listReplaceInPlaceHelp(bytes, index, element, element_width); + } else { + return listReplaceImmutable(bytes, length, alignment, index, element, element_width); + } +} + +inline fn listReplaceInPlaceHelp( + bytes: ?[*]u8, + index: usize, + element: Opaque, + element_width: usize, +) ?[*]u8 { + // the element we will replace + var element_at_index = (bytes orelse undefined) + (index * element_width); + + // decrement its refcount + // dec(element_at_index); + + // copy in the new element + @memcpy(element_at_index, element orelse undefined, element_width); + + return bytes; +} + +inline fn listReplaceImmutable( + old_bytes: ?[*]u8, + length: usize, + alignment: u32, + index: usize, + element: Opaque, + element_width: usize, +) ?[*]u8 { + const data_bytes = length * element_width; + + var new_bytes = utils.allocateWithRefcount(data_bytes, alignment); + + @memcpy(new_bytes, old_bytes orelse undefined, data_bytes); + + // the element we will replace + var element_at_index = new_bytes + (index * element_width); + + // decrement its refcount + // dec(element_at_index); + + // copy in the new element + @memcpy(element_at_index, element orelse undefined, element_width); + + // consume RC token of original + utils.decref(old_bytes, data_bytes, alignment); + + //return list; + return new_bytes; +} + pub fn listSetInPlace( bytes: ?[*]u8, index: usize, diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index c047588d92..8a23f09985 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -49,6 +49,8 @@ comptime { exportListFn(list.listConcat, "concat"); exportListFn(list.listSublist, "sublist"); exportListFn(list.listDropAt, "drop_at"); + exportListFn(list.listReplace, "replace"); + exportListFn(list.listReplaceInPlace, "replace_in_place"); exportListFn(list.listSet, "set"); exportListFn(list.listSetInPlace, "set_in_place"); exportListFn(list.listSwap, "swap"); diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 1345156d0f..c4c0b12e01 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -354,6 +354,8 @@ pub const LIST_RANGE: &str = "roc_builtins.list.range"; pub const LIST_REVERSE: &str = "roc_builtins.list.reverse"; pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with"; pub const LIST_CONCAT: &str = "roc_builtins.list.concat"; +pub const LIST_REPLACE: &str = "roc_builtins.list.replace"; +pub const LIST_REPLACE_IN_PLACE: &str = "roc_builtins.list.replace_in_place"; pub const LIST_SET: &str = "roc_builtins.list.set"; pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place"; pub const LIST_ANY: &str = "roc_builtins.list.any"; diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 97394b969b..35ab535a6a 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -4,9 +4,9 @@ use roc_module::symbol::Symbol; use roc_region::all::Region; use roc_types::builtin_aliases::{ bool_type, dec_type, dict_type, f32_type, f64_type, float_type, i128_type, i16_type, i32_type, - i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type, result_type, - set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type, u64_type, - u8_type, + i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type, pair_type, + result_type, set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type, + u64_type, u8_type, }; use roc_types::solved_types::SolvedType; use roc_types::subs::VarId; @@ -1034,7 +1034,7 @@ pub fn types() -> MutMap { add_top_level_function_type!( Symbol::LIST_GET, vec![list_type(flex(TVAR1)), nat_type()], - Box::new(result_type(flex(TVAR1), index_out_of_bounds)), + Box::new(result_type(flex(TVAR1), index_out_of_bounds.clone())), ); // first : List elem -> Result elem [ ListWasEmpty ]* @@ -1056,6 +1056,16 @@ pub fn types() -> MutMap { Box::new(result_type(flex(TVAR1), list_was_empty.clone())), ); + // replace : List elem, Nat, elem -> Result (Pair (List elem) elem) [ OutOfBounds ]* + add_top_level_function_type!( + Symbol::LIST_REPLACE, + vec![list_type(flex(TVAR1)), nat_type(), flex(TVAR1)], + Box::new(result_type( + pair_type(list_type(flex(TVAR1)), flex(TVAR1)), + index_out_of_bounds + )), + ); + // set : List elem, Nat, elem -> List elem add_top_level_function_type!( Symbol::LIST_SET, diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index a8228c6ecb..ae6375ada7 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -102,6 +102,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option STR_TO_I8 => str_to_num, LIST_LEN => list_len, LIST_GET => list_get, + LIST_REPLACE => list_replace, LIST_SET => list_set, LIST_APPEND => list_append, LIST_FIRST => list_first, @@ -2303,6 +2304,97 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } +/// List.replace : List elem, Nat, elem -> Result (Pair (List elem) elem) [ OutOfBounds ]* +/// +/// List.replace : +/// Attr (w | u | v) (List (Attr u a)), +/// Attr * Int, +/// Attr (u | v) a +/// -> Attr * (List (Attr u a)) +/// -> Attr * (Result (Pair (List (Attr u a)) Attr u a)) (Attr * [ OutOfBounds ]*)) +fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { + let arg_list = Symbol::ARG_1; + let arg_index = Symbol::ARG_2; + let arg_elem = Symbol::ARG_3; + let bool_var = var_store.fresh(); + let len_var = var_store.fresh(); + let elem_var = var_store.fresh(); + let list_arg_var = var_store.fresh(); + let ret_pair_var = var_store.fresh(); + + // Perform a bounds check. If it passes, run LowLevel::ListReplace. + // Otherwise, return the list unmodified. + let body = If { + cond_var: bool_var, + branch_var: ret_pair_var, + branches: vec![( + // if-condition + no_region( + // index < List.len list + RunLowLevel { + op: LowLevel::NumLt, + args: vec![ + (len_var, Var(arg_index)), + ( + len_var, + RunLowLevel { + op: LowLevel::ListLen, + args: vec![(list_arg_var, Var(arg_list))], + ret_var: len_var, + }, + ), + ], + ret_var: bool_var, + }, + ), + // then-branch + no_region( + // Ok + tag( + "Ok", + vec![ + // TODO: This should probably call get and then build the pair + // List.replaceUnsafe list index elem + RunLowLevel { + op: LowLevel::ListReplace, + args: vec![ + (list_arg_var, Var(arg_list)), + (len_var, Var(arg_index)), + (elem_var, Var(arg_elem)), + ], + ret_var: ret_pair_var, + }, + ], + var_store, + ), + ), + )], + final_else: Box::new( + // else-branch + no_region( + // Err + tag( + "Err", + vec![tag("OutOfBounds", Vec::new(), var_store)], + var_store, + ), + ), + ), + }; + + defn( + symbol, + vec![ + (list_arg_var, Symbol::ARG_1), + (len_var, Symbol::ARG_2), + (elem_var, Symbol::ARG_3), + ], + var_store, + body, + ret_pair_var, + ) +} + /// List.set : List elem, Nat, elem -> List elem /// /// List.set : @@ -2347,7 +2439,7 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { ), // then-branch no_region( - // List.setUnsafe list index + // List.setUnsafe list index elem RunLowLevel { op: LowLevel::ListSet, args: vec![ diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index af42090989..b44f84767b 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -13,8 +13,8 @@ use crate::llvm::build_list::{ self, allocate_list, empty_polymorphic_list, list_all, list_any, list_append, list_concat, list_contains, list_drop_at, list_find_unsafe, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4, - list_map_with_index, list_prepend, list_range, list_repeat, list_reverse, list_set, - list_single, list_sort_with, list_sublist, list_swap, + list_map_with_index, list_prepend, list_range, list_repeat, list_replace, list_reverse, + list_set, list_single, list_sort_with, list_sublist, list_swap, }; use crate::llvm::build_str::{ str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8, @@ -5653,6 +5653,21 @@ fn run_low_level<'a, 'ctx, 'env>( wrapper_struct, ) } + ListReplace => { + let list = load_symbol(scope, &args[0]); + let index = load_symbol(scope, &args[1]); + let (element, element_layout) = load_symbol_and_layout(scope, &args[2]); + + list_replace( + env, + layout_ids, + list, + index.into_int_value(), + element, + element_layout, + update_mode, + ) + } ListSet => { let list = load_symbol(scope, &args[0]); let index = load_symbol(scope, &args[1]); diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index 3f865b77aa..b27316a443 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -291,6 +291,50 @@ pub fn list_drop_at<'a, 'ctx, 'env>( ) } +/// List.replace : List elem, Nat, elem -> List elem +pub fn list_replace<'a, 'ctx, 'env>( + env: &Env<'a, 'ctx, 'env>, + layout_ids: &mut LayoutIds<'a>, + list: BasicValueEnum<'ctx>, + index: IntValue<'ctx>, + element: BasicValueEnum<'ctx>, + element_layout: &Layout<'a>, + update_mode: UpdateMode, +) -> BasicValueEnum<'ctx> { + let (length, bytes) = load_list( + env.builder, + list.into_struct_value(), + env.context.i8_type().ptr_type(AddressSpace::Generic), + ); + + let new_bytes = match update_mode { + UpdateMode::InPlace => call_bitcode_fn( + env, + &[ + bytes.into(), + index.into(), + pass_element_as_opaque(env, element, *element_layout), + layout_width(env, element_layout), + ], + bitcode::LIST_REPLACE_IN_PLACE, + ), + UpdateMode::Immutable => call_bitcode_fn( + env, + &[ + bytes.into(), + length.into(), + env.alignment_intvalue(element_layout), + index.into(), + pass_element_as_opaque(env, element, *element_layout), + layout_width(env, element_layout), + ], + bitcode::LIST_REPLACE, + ), + }; + + store_list(env, new_bytes.into_pointer_value(), length) +} + /// List.set : List elem, Nat, elem -> List elem pub fn list_set<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, diff --git a/compiler/gen_wasm/src/low_level.rs b/compiler/gen_wasm/src/low_level.rs index a5875e1813..8d71d3f484 100644 --- a/compiler/gen_wasm/src/low_level.rs +++ b/compiler/gen_wasm/src/low_level.rs @@ -260,14 +260,14 @@ impl<'a> LowLevelCall<'a> { _ => internal_error!("invalid storage for List"), }, - ListGetUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat - | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap - | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk - | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith - | ListSublist | ListDropAt | ListSwap | ListAny | ListAll | ListFindUnsafe - | DictSize | DictEmpty | DictInsert | DictRemove | DictContains | DictGetUnsafe - | DictKeys | DictValues | DictUnion | DictIntersection | DictDifference | DictWalk - | SetFromList => { + ListGetUnsafe | ListReplace | ListSet | ListSingle | ListRepeat | ListReverse + | ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange + | ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf + | ListWalk | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs + | ListSortWith | ListSublist | ListDropAt | ListSwap | ListAny | ListAll + | ListFindUnsafe | DictSize | DictEmpty | DictInsert | DictRemove | DictContains + | DictGetUnsafe | DictKeys | DictValues | DictUnion | DictIntersection + | DictDifference | DictWalk | SetFromList => { todo!("{:?}", self.lowlevel); } diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index be5a8394d3..720382ce1a 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -28,6 +28,7 @@ pub enum LowLevel { ListSet, ListSingle, ListRepeat, + ListReplace, ListReverse, ListConcat, ListContains, @@ -229,6 +230,7 @@ impl LowLevelWrapperType { Symbol::LIST_LEN => CanBeReplacedBy(ListLen), Symbol::LIST_GET => WrapperIsRequired, Symbol::LIST_SET => WrapperIsRequired, + Symbol::LIST_REPLACE => WrapperIsRequired, Symbol::LIST_SINGLE => CanBeReplacedBy(ListSingle), Symbol::LIST_REPEAT => CanBeReplacedBy(ListRepeat), Symbol::LIST_REVERSE => CanBeReplacedBy(ListReverse), diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 82778fa6d0..80d4bd235c 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -1141,6 +1141,7 @@ define_builtins! { 55 LIST_SORT_ASC: "sortAsc" 56 LIST_SORT_DESC: "sortDesc" 57 LIST_SORT_DESC_COMPARE: "#sortDescCompare" + 58 LIST_REPLACE: "replace" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index 3f6a0bbc4e..d8ed8fa610 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -935,6 +935,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { match op { ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]), ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), + ListReplace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListConcat => arena.alloc_slice_copy(&[owned, owned]), StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 333bdb4a63..ba8270808e 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1702,6 +1702,40 @@ fn get_int_list_oob() { ); } +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn replace_unique_int_list() { + assert_evals_to!( + indoc!( + r#" + result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 + when result is + Ok (Pair newList _) -> newList + Err _ -> [] + "# + ), + RocList::from_slice(&[12, 9, 33, 1, 5]), + RocList + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn replace_unique_int_list_get_old_value() { + assert_evals_to!( + indoc!( + r#" + result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 + when result is + Ok (Pair _ oldValue) -> oldValue + Err _ -> -1 + "# + ), + 7, + i64 + ); +} + #[test] #[cfg(any(feature = "gen-llvm"))] fn get_set_unique_int_list() { diff --git a/compiler/types/src/builtin_aliases.rs b/compiler/types/src/builtin_aliases.rs index f8df53a256..07efa07c9e 100644 --- a/compiler/types/src/builtin_aliases.rs +++ b/compiler/types/src/builtin_aliases.rs @@ -914,6 +914,15 @@ pub fn ordering_type() -> SolvedType { ) } +#[inline(always)] +pub fn pair_type(t1: SolvedType, t2: SolvedType) -> SolvedType { + // [ Pair t1 t2 ] + SolvedType::TagUnion( + vec![(TagName::Global("Pair".into()), vec![t1, t2])], + Box::new(SolvedType::EmptyTagUnion), + ) +} + #[inline(always)] pub fn result_type(a: SolvedType, e: SolvedType) -> SolvedType { SolvedType::Alias( From 27b47713aa5ceae1b110d55365b59fca556f0525 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 20:40:45 -0800 Subject: [PATCH 004/132] Add some comments and TODOs --- compiler/builtins/bitcode/src/list.zig | 8 ++++++++ compiler/gen_llvm/src/llvm/build_list.rs | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 012f24b141..7723a59668 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -1301,6 +1301,10 @@ inline fn listReplaceInPlaceHelp( element: Opaque, element_width: usize, ) ?[*]u8 { + // TODO: figure out how to return an element and a List. + // We only know the elment size at runtime. + // This code is currently the same as listSet. + // the element we will replace var element_at_index = (bytes orelse undefined) + (index * element_width); @@ -1321,6 +1325,10 @@ inline fn listReplaceImmutable( element: Opaque, element_width: usize, ) ?[*]u8 { + // TODO: figure out how to return an element and a List. + // We only know the elment size at runtime. + // This code is currently the same as listSet. + const data_bytes = length * element_width; var new_bytes = utils.allocateWithRefcount(data_bytes, alignment); diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index b27316a443..fa709380a5 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -294,19 +294,22 @@ pub fn list_drop_at<'a, 'ctx, 'env>( /// List.replace : List elem, Nat, elem -> List elem pub fn list_replace<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, + _layout_ids: &mut LayoutIds<'a>, list: BasicValueEnum<'ctx>, index: IntValue<'ctx>, element: BasicValueEnum<'ctx>, element_layout: &Layout<'a>, update_mode: UpdateMode, ) -> BasicValueEnum<'ctx> { + // TODO: This or elsewhere needs to deal with building the record that gets returned. let (length, bytes) = load_list( env.builder, list.into_struct_value(), env.context.i8_type().ptr_type(AddressSpace::Generic), ); + // Assume the bounds have already been checked earlier + // (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe) let new_bytes = match update_mode { UpdateMode::InPlace => call_bitcode_fn( env, From dddf8ff785e9ff997d5d0fdaa7a32024aa8e1cd3 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 20:41:26 -0800 Subject: [PATCH 005/132] switch from pair to record and change name to ListReplaceUnsafe --- compiler/builtins/src/std.rs | 16 +++++++++++----- compiler/can/src/builtins.rs | 18 ++++++++---------- compiler/gen_llvm/src/llvm/build.rs | 6 +++--- compiler/gen_llvm/src/llvm/build_list.rs | 4 ++-- compiler/gen_wasm/src/low_level.rs | 2 +- compiler/module/src/low_level.rs | 2 +- compiler/mono/src/borrow.rs | 2 +- compiler/test_gen/src/gen_list.rs | 4 ++-- 8 files changed, 29 insertions(+), 25 deletions(-) diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 35ab535a6a..0ec5e0e28a 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -4,9 +4,9 @@ use roc_module::symbol::Symbol; use roc_region::all::Region; use roc_types::builtin_aliases::{ bool_type, dec_type, dict_type, f32_type, f64_type, float_type, i128_type, i16_type, i32_type, - i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type, pair_type, - result_type, set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type, - u64_type, u8_type, + i64_type, i8_type, int_type, list_type, nat_type, num_type, ordering_type, result_type, + set_type, str_type, str_utf8_byte_problem_type, u128_type, u16_type, u32_type, u64_type, + u8_type, }; use roc_types::solved_types::SolvedType; use roc_types::subs::VarId; @@ -1056,12 +1056,18 @@ pub fn types() -> MutMap { Box::new(result_type(flex(TVAR1), list_was_empty.clone())), ); - // replace : List elem, Nat, elem -> Result (Pair (List elem) elem) [ OutOfBounds ]* + // replace : List elem, Nat, elem -> Result { list: List elem, value: elem } [ OutOfBounds ]* add_top_level_function_type!( Symbol::LIST_REPLACE, vec![list_type(flex(TVAR1)), nat_type(), flex(TVAR1)], Box::new(result_type( - pair_type(list_type(flex(TVAR1)), flex(TVAR1)), + SolvedType::Record { + fields: vec![ + ("list".into(), RecordField::Required(list_type(flex(TVAR1)))), + ("value".into(), RecordField::Required(flex(TVAR1))), + ], + ext: Box::new(SolvedType::EmptyRecord), + }, index_out_of_bounds )), ); diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index ae6375ada7..ee035c30cd 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2304,14 +2304,13 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// List.replace : List elem, Nat, elem -> Result (Pair (List elem) elem) [ OutOfBounds ]* +/// List.replace : List elem, Nat, elem -> Result { list: List elem, value: elem } [ OutOfBounds ]* /// /// List.replace : /// Attr (w | u | v) (List (Attr u a)), /// Attr * Int, /// Attr (u | v) a -/// -> Attr * (List (Attr u a)) -/// -> Attr * (Result (Pair (List (Attr u a)) Attr u a)) (Attr * [ OutOfBounds ]*)) +/// -> Attr * (Result { list: List (Attr u a), value: Attr u a } (Attr * [ OutOfBounds ]*)) fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { let arg_list = Symbol::ARG_1; let arg_index = Symbol::ARG_2; @@ -2320,13 +2319,13 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { let len_var = var_store.fresh(); let elem_var = var_store.fresh(); let list_arg_var = var_store.fresh(); - let ret_pair_var = var_store.fresh(); + let ret_record_var = var_store.fresh(); - // Perform a bounds check. If it passes, run LowLevel::ListReplace. + // Perform a bounds check. If it passes, run LowLevel::ListReplaceUnsafe. // Otherwise, return the list unmodified. let body = If { cond_var: bool_var, - branch_var: ret_pair_var, + branch_var: ret_record_var, branches: vec![( // if-condition no_region( @@ -2353,16 +2352,15 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { tag( "Ok", vec![ - // TODO: This should probably call get and then build the pair // List.replaceUnsafe list index elem RunLowLevel { - op: LowLevel::ListReplace, + op: LowLevel::ListReplaceUnsafe, args: vec![ (list_arg_var, Var(arg_list)), (len_var, Var(arg_index)), (elem_var, Var(arg_elem)), ], - ret_var: ret_pair_var, + ret_var: ret_record_var, }, ], var_store, @@ -2391,7 +2389,7 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { ], var_store, body, - ret_pair_var, + ret_record_var, ) } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index b44f84767b..347bd2d044 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -13,7 +13,7 @@ use crate::llvm::build_list::{ self, allocate_list, empty_polymorphic_list, list_all, list_any, list_append, list_concat, list_contains, list_drop_at, list_find_unsafe, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4, - list_map_with_index, list_prepend, list_range, list_repeat, list_replace, list_reverse, + list_map_with_index, list_prepend, list_range, list_repeat, list_replace_unsafe, list_reverse, list_set, list_single, list_sort_with, list_sublist, list_swap, }; use crate::llvm::build_str::{ @@ -5653,12 +5653,12 @@ fn run_low_level<'a, 'ctx, 'env>( wrapper_struct, ) } - ListReplace => { + ListReplaceUnsafe => { let list = load_symbol(scope, &args[0]); let index = load_symbol(scope, &args[1]); let (element, element_layout) = load_symbol_and_layout(scope, &args[2]); - list_replace( + list_replace_unsafe( env, layout_ids, list, diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index fa709380a5..fd528bbac1 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -291,8 +291,8 @@ pub fn list_drop_at<'a, 'ctx, 'env>( ) } -/// List.replace : List elem, Nat, elem -> List elem -pub fn list_replace<'a, 'ctx, 'env>( +/// List.replace_unsafe : List elem, Nat, elem -> { list: List elem, value: elem } +pub fn list_replace_unsafe<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, _layout_ids: &mut LayoutIds<'a>, list: BasicValueEnum<'ctx>, diff --git a/compiler/gen_wasm/src/low_level.rs b/compiler/gen_wasm/src/low_level.rs index 8d71d3f484..b68d8e968a 100644 --- a/compiler/gen_wasm/src/low_level.rs +++ b/compiler/gen_wasm/src/low_level.rs @@ -260,7 +260,7 @@ impl<'a> LowLevelCall<'a> { _ => internal_error!("invalid storage for List"), }, - ListGetUnsafe | ListReplace | ListSet | ListSingle | ListRepeat | ListReverse + ListGetUnsafe | ListReplaceUnsafe | ListSet | ListSingle | ListRepeat | ListReverse | ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index 720382ce1a..ad10a1cbba 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -28,7 +28,7 @@ pub enum LowLevel { ListSet, ListSingle, ListRepeat, - ListReplace, + ListReplaceUnsafe, ListReverse, ListConcat, ListContains, diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index d8ed8fa610..d46ccbc8ee 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -935,7 +935,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { match op { ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]), ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), - ListReplace => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), + ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListConcat => arena.alloc_slice_copy(&[owned, owned]), StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index ba8270808e..296f083e59 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1710,7 +1710,7 @@ fn replace_unique_int_list() { r#" result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 when result is - Ok (Pair newList _) -> newList + Ok {list, value} -> list Err _ -> [] "# ), @@ -1727,7 +1727,7 @@ fn replace_unique_int_list_get_old_value() { r#" result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 when result is - Ok (Pair _ oldValue) -> oldValue + Ok {list, value} -> value Err _ -> -1 "# ), From dc59ba97c2eed6177f19ebf5f6ddb8c9a3d49270 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 22:02:23 -0800 Subject: [PATCH 006/132] change listReplace zig builtin to use RocList --- compiler/builtins/bitcode/src/list.zig | 73 ++++++-------------------- 1 file changed, 15 insertions(+), 58 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 7723a59668..6ad31ced7c 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -1257,98 +1257,55 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt } pub fn listReplaceInPlace( - bytes: ?[*]u8, + list: RocList index: usize, element: Opaque, element_width: usize, -) callconv(.C) ?[*]u8 { + out_element: ?[*]u8, +) callconv(.C) extern RocList { // INVARIANT: bounds checking happens on the roc side // // at the time of writing, the function is implemented roughly as // `if inBounds then LowLevelListReplace input index item else input` // so we don't do a bounds check here. Hence, the list is also non-empty, // because inserting into an empty list is always out of bounds - - return listReplaceInPlaceHelp(bytes, index, element, element_width); + return listReplaceInPlaceHelp(list, index, element, element_width, out_element); } pub fn listReplace( - bytes: ?[*]u8, - length: usize, + list: RocList alignment: u32, index: usize, element: Opaque, element_width: usize, -) callconv(.C) ?[*]u8 { + out_element: ?[*]u8, +) callconv(.C) RocList { // INVARIANT: bounds checking happens on the roc side // // at the time of writing, the function is implemented roughly as // `if inBounds then LowLevelListReplace input index item else input` // so we don't do a bounds check here. Hence, the list is also non-empty, // because inserting into an empty list is always out of bounds - const ptr: [*]usize = @ptrCast([*]usize, @alignCast(@alignOf(usize), bytes)); - - if ((ptr - 1)[0] == utils.REFCOUNT_ONE) { - return listReplaceInPlaceHelp(bytes, index, element, element_width); - } else { - return listReplaceImmutable(bytes, length, alignment, index, element, element_width); - } + listReplaceInPlaceHelp(list.makeUnique(alignment, element_width), index, element, element_width, out_element); } inline fn listReplaceInPlaceHelp( - bytes: ?[*]u8, + list: RocList index: usize, element: Opaque, element_width: usize, -) ?[*]u8 { - // TODO: figure out how to return an element and a List. - // We only know the elment size at runtime. - // This code is currently the same as listSet. - + out_element: ?[*]u8, +) extern struct RocList { // the element we will replace - var element_at_index = (bytes orelse undefined) + (index * element_width); + var element_at_index = (list.bytes orelse undefined) + (index * element_width); - // decrement its refcount - // dec(element_at_index); + // copy out the old element + @memcpy(out_element orelse undefined, element_at_index, element_width); // copy in the new element @memcpy(element_at_index, element orelse undefined, element_width); - return bytes; -} - -inline fn listReplaceImmutable( - old_bytes: ?[*]u8, - length: usize, - alignment: u32, - index: usize, - element: Opaque, - element_width: usize, -) ?[*]u8 { - // TODO: figure out how to return an element and a List. - // We only know the elment size at runtime. - // This code is currently the same as listSet. - - const data_bytes = length * element_width; - - var new_bytes = utils.allocateWithRefcount(data_bytes, alignment); - - @memcpy(new_bytes, old_bytes orelse undefined, data_bytes); - - // the element we will replace - var element_at_index = new_bytes + (index * element_width); - - // decrement its refcount - // dec(element_at_index); - - // copy in the new element - @memcpy(element_at_index, element orelse undefined, element_width); - - // consume RC token of original - utils.decref(old_bytes, data_bytes, alignment); - - //return list; - return new_bytes; + return list; } pub fn listSetInPlace( From aff962809b2bd180228ee31a392c8f1d3c5065a6 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 22:33:12 -0800 Subject: [PATCH 007/132] call ListReplace and generate struct afterwards --- compiler/builtins/bitcode/src/list.zig | 12 +++---- compiler/gen_llvm/src/llvm/build_list.rs | 45 ++++++++++++++++++------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 6ad31ced7c..299f4f1591 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -1257,12 +1257,12 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: u32, element_widt } pub fn listReplaceInPlace( - list: RocList + list: RocList, index: usize, element: Opaque, element_width: usize, out_element: ?[*]u8, -) callconv(.C) extern RocList { +) callconv(.C) RocList { // INVARIANT: bounds checking happens on the roc side // // at the time of writing, the function is implemented roughly as @@ -1273,7 +1273,7 @@ pub fn listReplaceInPlace( } pub fn listReplace( - list: RocList + list: RocList, alignment: u32, index: usize, element: Opaque, @@ -1286,16 +1286,16 @@ pub fn listReplace( // `if inBounds then LowLevelListReplace input index item else input` // so we don't do a bounds check here. Hence, the list is also non-empty, // because inserting into an empty list is always out of bounds - listReplaceInPlaceHelp(list.makeUnique(alignment, element_width), index, element, element_width, out_element); + return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width), index, element, element_width, out_element); } inline fn listReplaceInPlaceHelp( - list: RocList + list: RocList, index: usize, element: Opaque, element_width: usize, out_element: ?[*]u8, -) extern struct RocList { +) RocList { // the element we will replace var element_at_index = (list.bytes orelse undefined) + (index * element_width); diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index fd528bbac1..997e322411 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -301,41 +301,64 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( element_layout: &Layout<'a>, update_mode: UpdateMode, ) -> BasicValueEnum<'ctx> { - // TODO: This or elsewhere needs to deal with building the record that gets returned. - let (length, bytes) = load_list( - env.builder, - list.into_struct_value(), - env.context.i8_type().ptr_type(AddressSpace::Generic), - ); + let element_type = basic_type_from_layout(env, &element_layout); + let element_ptr = env + .builder + .build_alloca(element_type, "output_element_as_opaque"); // Assume the bounds have already been checked earlier // (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe) - let new_bytes = match update_mode { + let new_list = match update_mode { UpdateMode::InPlace => call_bitcode_fn( env, &[ - bytes.into(), + list.into(), index.into(), pass_element_as_opaque(env, element, *element_layout), layout_width(env, element_layout), + pass_as_opaque(env, element_ptr), ], bitcode::LIST_REPLACE_IN_PLACE, ), UpdateMode::Immutable => call_bitcode_fn( env, &[ - bytes.into(), - length.into(), + list.into(), env.alignment_intvalue(element_layout), index.into(), pass_element_as_opaque(env, element, *element_layout), layout_width(env, element_layout), + pass_as_opaque(env, element_ptr), ], bitcode::LIST_REPLACE, ), }; - store_list(env, new_bytes.into_pointer_value(), length) + // Load the element and returned list into a struct. + let old_element = env.builder.build_load(element_ptr, "load_element"); + + let result = env + .context + .struct_type( + // TODO: does the order need to be decided by the size of the element type. + &[ + super::convert::zig_list_type(env).into(), + element_type.into(), + ], + false, + ) + .const_zero(); + + let result = env + .builder + .build_insert_value(result, old_element, 0, "insert_value") + .unwrap(); + + env.builder + .build_insert_value(result, new_list, 1, "insert_list") + .unwrap() + .into_struct_value() + .into() } /// List.set : List elem, Nat, elem -> List elem From edfbd6242f0e64aff6f93dbd6ce99a6d5a705f94 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 22:38:32 -0800 Subject: [PATCH 008/132] fix ListReplace generate if types --- compiler/can/src/builtins.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index ee035c30cd..550c7d0d0f 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2320,12 +2320,13 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { let elem_var = var_store.fresh(); let list_arg_var = var_store.fresh(); let ret_record_var = var_store.fresh(); + let ret_result_var = var_store.fresh(); // Perform a bounds check. If it passes, run LowLevel::ListReplaceUnsafe. // Otherwise, return the list unmodified. let body = If { cond_var: bool_var, - branch_var: ret_record_var, + branch_var: var_store.fresh(), branches: vec![( // if-condition no_region( @@ -2389,7 +2390,7 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { ], var_store, body, - ret_record_var, + ret_result_var, ) } From 889b189191a0cc1b5ab1b88f63bf43a2a6caddaf Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 22:46:50 -0800 Subject: [PATCH 009/132] fix list passing --- compiler/gen_llvm/src/llvm/build_list.rs | 4 ++-- compiler/test_gen/src/gen_list.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index 997e322411..c6d39c33d5 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -312,7 +312,7 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( UpdateMode::InPlace => call_bitcode_fn( env, &[ - list.into(), + pass_list_cc(env, list), index.into(), pass_element_as_opaque(env, element, *element_layout), layout_width(env, element_layout), @@ -323,7 +323,7 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( UpdateMode::Immutable => call_bitcode_fn( env, &[ - list.into(), + pass_list_cc(env, list), env.alignment_intvalue(element_layout), index.into(), pass_element_as_opaque(env, element, *element_layout), diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 296f083e59..18ce3051b5 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1710,7 +1710,7 @@ fn replace_unique_int_list() { r#" result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 when result is - Ok {list, value} -> list + Ok {list} -> list Err _ -> [] "# ), @@ -1727,7 +1727,7 @@ fn replace_unique_int_list_get_old_value() { r#" result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 when result is - Ok {list, value} -> value + Ok {value} -> value Err _ -> -1 "# ), From badad1c1ad49d5f03f0bfc57231e4d3eb05854d9 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 22:50:28 -0800 Subject: [PATCH 010/132] get basic tests passing --- compiler/gen_llvm/src/llvm/build_list.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index c6d39c33d5..483a07755b 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -309,7 +309,7 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( // Assume the bounds have already been checked earlier // (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe) let new_list = match update_mode { - UpdateMode::InPlace => call_bitcode_fn( + UpdateMode::InPlace => call_list_bitcode_fn( env, &[ pass_list_cc(env, list), @@ -320,7 +320,7 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( ], bitcode::LIST_REPLACE_IN_PLACE, ), - UpdateMode::Immutable => call_bitcode_fn( + UpdateMode::Immutable => call_list_bitcode_fn( env, &[ pass_list_cc(env, list), @@ -351,11 +351,11 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( let result = env .builder - .build_insert_value(result, old_element, 0, "insert_value") + .build_insert_value(result, new_list, 0, "insert_list") .unwrap(); env.builder - .build_insert_value(result, new_list, 1, "insert_list") + .build_insert_value(result, old_element, 1, "insert_value") .unwrap() .into_struct_value() .into() From 116b585cdccb95897aefd2988ed1ca14c84732f6 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Thu, 24 Feb 2022 22:59:47 -0800 Subject: [PATCH 011/132] add more tests --- compiler/gen_llvm/src/llvm/build_list.rs | 1 - compiler/test_gen/src/gen_list.rs | 51 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index 483a07755b..ede3e6430a 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -340,7 +340,6 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( let result = env .context .struct_type( - // TODO: does the order need to be decided by the size of the element type. &[ super::convert::zig_list_type(env).into(), element_type.into(), diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 18ce3051b5..2e3670c7fe 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1736,6 +1736,57 @@ fn replace_unique_int_list_get_old_value() { ); } +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn replace_unique_get_large_value() { + assert_evals_to!( + indoc!( + r#" + list : List { a : U64, b: U64, c: U64, d: U64 } + list = [ { a: 1, b: 2, c: 3, d: 4 }, { a: 5, b: 6, c: 7, d: 8 }, { a: 9, b: 10, c: 11, d: 12 } ] + result = List.replace list 1 { a: 13, b: 14, c: 15, d: 16 } + when result is + Ok {value} -> value + Err _ -> { a: 0, b: 0, c: 0, d: 0 } + "# + ), + (5, 6, 7, 8), + (u64, u64, u64, u64) + ); +} + +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn replace_shared_int_list() { + assert_evals_to!( + indoc!( + r#" + wrapper = \shared -> + # This should not mutate the original + replaced = + when List.replace shared 1 7.7 is + Ok {list} -> list + Err _ -> [] + x = + when List.get replaced 1 is + Ok num -> num + Err _ -> 0 + + y = + when List.get shared 1 is + Ok num -> num + Err _ -> 0 + + { x, y } + + wrapper [ 2.1, 4.3 ] + "# + ), + (7.7, 4.3), + (f64, f64) + ); +} + #[test] #[cfg(any(feature = "gen-llvm"))] fn get_set_unique_int_list() { From 2682193b76e1be512fae4865597387589d141bb9 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 26 Feb 2022 13:31:08 -0800 Subject: [PATCH 012/132] Fix compile error with --features=parse_debug_trace --- compiler/parse/src/parser.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index d29b62f85b..8cfa8b2444 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -710,9 +710,8 @@ where let cur_indent = INDENT.with(|i| *i.borrow()); println!( - "@{:>5}:{:<5}: {}{:<50}", - state.line, - state.column, + "{:>5?}: {}{:<50}", + state.pos(), &indent_text[..cur_indent * 2], self.message ); @@ -727,9 +726,8 @@ where }; println!( - "@{:>5}:{:<5}: {}{:<50} {:<15} {:?}", - state.line, - state.column, + "{:<5?}: {}{:<50} {:<15} {:?}", + state.pos(), &indent_text[..cur_indent * 2], self.message, format!("{:?}", progress), From 0e3b9e56241fd4774090de28e048e78d9c0673de Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 26 Feb 2022 13:31:45 -0800 Subject: [PATCH 013/132] Allow incorrect indents for closing braces, to be fixed in the formatter --- compiler/parse/src/parser.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 8cfa8b2444..8f9c463609 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1214,7 +1214,11 @@ macro_rules! collection_trailing_sep_e { $indent_problem ) ), - $crate::blankspace::space0_e($min_indent, $indent_problem) + $crate::blankspace::space0_e( + // we use min_indent=0 because we want to parse incorrectly indented closing braces + // and later fix these up in the formatter. + 0 /* min_indent */, + $indent_problem) ).parse(arena, state)?; let (_,_, state) = From 333234aad90ee3720762e83b8cdfc6751f0a1d00 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 26 Feb 2022 13:32:20 -0800 Subject: [PATCH 014/132] Improve error message for no_extra_snapshot_test_files test --- compiler/parse/tests/test_parse.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 7af4ab53fc..544b48fafe 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -75,19 +75,32 @@ mod test_parse { let mut base = std::path::PathBuf::from("tests"); base.push("snapshots"); let pass_or_fail_names = list(&base); + let mut extra_test_files = std::collections::HashSet::new(); for res in pass_or_fail_names { assert!(res == "pass" || res == "fail"); let res_dir = base.join(&res); for file in list(&res_dir) { - if let Some(file) = file.strip_suffix(".roc") { - assert!(tests.contains(format!("{}/{}", &res, file).as_str()), "{}", file); - } else if let Some(file) = file.strip_suffix(".result-ast") { - assert!(tests.contains(format!("{}/{}", &res, file).as_str()), "{}", file); + let test = if let Some(test) = file.strip_suffix(".roc") { + test + } else if let Some(test) = file.strip_suffix(".result-ast") { + test } else { - panic!("unexpected test file found: {}", file); + panic!("unexpected file found in tests/snapshots: {}", file); + }; + let test_name = format!("{}/{}", &res, test); + if !tests.contains(test_name.as_str()) { + extra_test_files.insert(test_name); } } } + + if extra_test_files.len() > 0 { + eprintln!("Found extra test files:"); + for file in extra_test_files { + eprintln!("{}", file); + } + panic!("Add entries for these in the `snapshot_tests!` macro in test_parse.rs"); + } } $( From b2bb9dcd652b83c52bc3f7828fb14f52c77aa8a8 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 26 Feb 2022 13:32:47 -0800 Subject: [PATCH 015/132] improve parser snapshot test error messages --- compiler/parse/tests/test_parse.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index 544b48fafe..a9cebdbd10 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -291,15 +291,11 @@ mod test_parse { let result = func(&input); let actual_result = if should_pass { - eprintln!("The source code for this test did not successfully parse!\n"); - - result.unwrap() + result.expect("The source code for this test did not successfully parse!") } else { - eprintln!( - "The source code for this test successfully parsed, but it was not expected to!\n" - ); - - result.unwrap_err() + result.expect_err( + "The source code for this test successfully parsed, but it was not expected to!", + ) }; if std::env::var("ROC_PARSER_SNAPSHOT_TEST_OVERWRITE").is_ok() { From ab563fc9a586be3fbf28f5505281a528567e00a1 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 26 Feb 2022 13:33:21 -0800 Subject: [PATCH 016/132] Remove dead code --- compiler/parse/src/parser.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index 8f9c463609..3a57f32f85 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -1405,21 +1405,6 @@ where } } -pub fn check_indent<'a, TE, E>(min_indent: u32, to_problem: TE) -> impl Parser<'a, (), E> -where - TE: Fn(Position) -> E, - E: 'a, -{ - move |_arena, state: State<'a>| { - dbg!(state.indent_column, min_indent); - if state.indent_column < min_indent { - Err((NoProgress, to_problem(state.pos()), state)) - } else { - Ok((NoProgress, (), state)) - } - } -} - #[macro_export] macro_rules! word1_check_indent { ($word:expr, $word_problem:expr, $min_indent:expr, $indent_problem:expr) => { From 611d1784a9093889da368575b2ff13f9b2494d4a Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 26 Feb 2022 13:33:33 -0800 Subject: [PATCH 017/132] Add tests --- ..._closing_indent_not_enough.expr.result-ast | 74 +++++++++++++++++++ .../list_closing_indent_not_enough.expr.roc | 9 +++ ...e_indent_no_trailing_comma.expr.result-ast | 42 +++++++++++ ...ing_same_indent_no_trailing_comma.expr.roc | 5 ++ ...indent_with_trailing_comma.expr.result-ast | 42 +++++++++++ ...g_same_indent_with_trailing_comma.expr.roc | 5 ++ compiler/parse/tests/test_parse.rs | 3 + 7 files changed, 180 insertions(+) create mode 100644 compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast create mode 100644 compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.roc create mode 100644 compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast create mode 100644 compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.roc create mode 100644 compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast create mode 100644 compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.roc diff --git a/compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast b/compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast new file mode 100644 index 0000000000..74ccc748aa --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast @@ -0,0 +1,74 @@ +Defs( + [ + @0-58 Body( + @0-7 Malformed( + "my_list", + ), + @10-58 List( + Collection { + items: [ + @16-17 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], + ), + @23-48 SpaceBefore( + List( + Collection { + items: [ + @33-34 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + ], + ), + @44-45 SpaceBefore( + Var { + module_name: "", + ident: "b", + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + [ + Newline, + ], + ), + @54-55 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ), + ], + @59-61 SpaceBefore( + Num( + "42", + ), + [ + Newline, + ], + ), +) diff --git a/compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.roc b/compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.roc new file mode 100644 index 0000000000..cdfc50f2e0 --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/list_closing_indent_not_enough.expr.roc @@ -0,0 +1,9 @@ +my_list = [ + 0, + [ + a, + b, +], + 1, +] +42 diff --git a/compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast new file mode 100644 index 0000000000..5af6f4259c --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast @@ -0,0 +1,42 @@ +Defs( + [ + @0-26 Body( + @0-7 Malformed( + "my_list", + ), + @10-26 List( + Collection { + items: [ + @16-17 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], + ), + @23-24 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ), + ], + @27-29 SpaceBefore( + Num( + "42", + ), + [ + Newline, + ], + ), +) diff --git a/compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.roc b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.roc new file mode 100644 index 0000000000..24b7269dec --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.roc @@ -0,0 +1,5 @@ +my_list = [ + 0, + 1 +] +42 diff --git a/compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast new file mode 100644 index 0000000000..99d1bf828f --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast @@ -0,0 +1,42 @@ +Defs( + [ + @0-27 Body( + @0-7 Malformed( + "my_list", + ), + @10-27 List( + Collection { + items: [ + @16-17 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], + ), + @23-24 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ), + ], + @28-30 SpaceBefore( + Num( + "42", + ), + [ + Newline, + ], + ), +) diff --git a/compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.roc b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.roc new file mode 100644 index 0000000000..f6e475d2df --- /dev/null +++ b/compiler/parse/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.roc @@ -0,0 +1,5 @@ +my_list = [ + 0, + 1, +] +42 diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index a9cebdbd10..6b7208dd13 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -122,6 +122,7 @@ mod test_parse { snapshot_tests! { fail/type_argument_no_arrow.expr, fail/type_double_comma.expr, + pass/list_closing_indent_not_enough.expr, pass/add_var_with_spaces.expr, pass/add_with_spaces.expr, pass/annotated_record_destructure.expr, @@ -167,6 +168,8 @@ mod test_parse { pass/int_with_underscore.expr, pass/interface_with_newline.header, pass/lowest_float.expr, + pass/list_closing_same_indent_no_trailing_comma.expr, + pass/list_closing_same_indent_with_trailing_comma.expr, pass/lowest_int.expr, pass/malformed_ident_due_to_underscore.expr, pass/malformed_pattern_field_access.expr, // See https://github.com/rtfeldman/roc/issues/399 From d9e9c28889e65ba8f960909738e75195998f67b0 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 27 Feb 2022 00:45:51 -0800 Subject: [PATCH 018/132] add error test case --- compiler/can/src/builtins.rs | 2 +- compiler/test_gen/src/gen_list.rs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 550c7d0d0f..dbc5063359 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2326,7 +2326,7 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { // Otherwise, return the list unmodified. let body = If { cond_var: bool_var, - branch_var: var_store.fresh(), + branch_var: ret_result_var, branches: vec![( // if-condition no_region( diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index ba4cce5362..ac34090e7d 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1780,6 +1780,23 @@ fn replace_unique_int_list() { ); } +#[test] +#[cfg(any(feature = "gen-llvm"))] +fn replace_unique_int_list_out_of_bounds() { + assert_evals_to!( + indoc!( + r#" + result = List.replace [ 12, 9, 7, 1, 5 ] 5 33 + when result is + Ok {value} -> value + Err OutOfBounds -> -1 + "# + ), + -1, + i64 + ); +} + #[test] #[cfg(any(feature = "gen-llvm"))] fn replace_unique_int_list_get_old_value() { From 4d42d81c63a786d16a66e17d55ad58ddfc154734 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 27 Feb 2022 01:21:02 -0800 Subject: [PATCH 019/132] add broken attempt to get list.set to use list.replace under the hood --- compiler/can/src/builtins.rs | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index dbc5063359..acec8fc84c 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2408,6 +2408,7 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { let bool_var = var_store.fresh(); let len_var = var_store.fresh(); let elem_var = var_store.fresh(); + let replace_record_var = var_store.fresh(); let list_arg_var = var_store.fresh(); // Uniqueness type Attr differs between let list_ret_var = var_store.fresh(); // the arg list and the returned list @@ -2437,18 +2438,24 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { }, ), // then-branch - no_region( - // List.setUnsafe list index elem - RunLowLevel { - op: LowLevel::ListSet, - args: vec![ - (list_arg_var, Var(arg_list)), - (len_var, Var(arg_index)), - (elem_var, Var(arg_elem)), - ], - ret_var: list_ret_var, - }, - ), + no_region(Access { + record_var: replace_record_var, + ext_var: var_store.fresh(), + field_var: list_ret_var, + loc_expr: Box::new(no_region( + // List.replaceUnsafe list index elem + RunLowLevel { + op: LowLevel::ListReplaceUnsafe, + args: vec![ + (list_arg_var, Var(arg_list)), + (len_var, Var(arg_index)), + (elem_var, Var(arg_elem)), + ], + ret_var: replace_record_var, + }, + )), + field: "list".into(), + }), )], final_else: Box::new( // else-branch From 78fe73411375332fce48a0169645b5e7fd16ffe5 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 27 Feb 2022 22:47:08 +0100 Subject: [PATCH 020/132] change the return type of List.replace --- compiler/builtins/src/std.rs | 19 +++++------- compiler/can/src/builtins.rs | 57 ++++++++++++++++-------------------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 0ec5e0e28a..d9044f4f6e 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -1056,20 +1056,17 @@ pub fn types() -> MutMap { Box::new(result_type(flex(TVAR1), list_was_empty.clone())), ); - // replace : List elem, Nat, elem -> Result { list: List elem, value: elem } [ OutOfBounds ]* + // replace : List elem, Nat, elem -> { list: List elem, value: elem } add_top_level_function_type!( Symbol::LIST_REPLACE, vec![list_type(flex(TVAR1)), nat_type(), flex(TVAR1)], - Box::new(result_type( - SolvedType::Record { - fields: vec![ - ("list".into(), RecordField::Required(list_type(flex(TVAR1)))), - ("value".into(), RecordField::Required(flex(TVAR1))), - ], - ext: Box::new(SolvedType::EmptyRecord), - }, - index_out_of_bounds - )), + Box::new(SolvedType::Record { + fields: vec![ + ("list".into(), RecordField::Required(list_type(flex(TVAR1)))), + ("value".into(), RecordField::Required(flex(TVAR1))), + ], + ext: Box::new(SolvedType::EmptyRecord), + }), ); // set : List elem, Nat, elem -> List elem diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index acec8fc84c..3e26eb573e 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -2304,13 +2304,7 @@ fn list_get(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } -/// List.replace : List elem, Nat, elem -> Result { list: List elem, value: elem } [ OutOfBounds ]* -/// -/// List.replace : -/// Attr (w | u | v) (List (Attr u a)), -/// Attr * Int, -/// Attr (u | v) a -/// -> Attr * (Result { list: List (Attr u a), value: Attr u a } (Attr * [ OutOfBounds ]*)) +/// List.replace : List elem, Nat, elem -> { list: List elem, value: elem } fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { let arg_list = Symbol::ARG_1; let arg_index = Symbol::ARG_2; @@ -2322,6 +2316,18 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { let ret_record_var = var_store.fresh(); let ret_result_var = var_store.fresh(); + let list_field = Field { + var: list_arg_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::Var(arg_list))), + }; + + let value_field = Field { + var: elem_var, + region: Region::zero(), + loc_expr: Box::new(Loc::at_zero(Expr::Var(arg_elem))), + }; + // Perform a bounds check. If it passes, run LowLevel::ListReplaceUnsafe. // Otherwise, return the list unmodified. let body = If { @@ -2349,35 +2355,24 @@ fn list_replace(symbol: Symbol, var_store: &mut VarStore) -> Def { ), // then-branch no_region( - // Ok - tag( - "Ok", - vec![ - // List.replaceUnsafe list index elem - RunLowLevel { - op: LowLevel::ListReplaceUnsafe, - args: vec![ - (list_arg_var, Var(arg_list)), - (len_var, Var(arg_index)), - (elem_var, Var(arg_elem)), - ], - ret_var: ret_record_var, - }, + // List.replaceUnsafe list index elem + RunLowLevel { + op: LowLevel::ListReplaceUnsafe, + args: vec![ + (list_arg_var, Var(arg_list)), + (len_var, Var(arg_index)), + (elem_var, Var(arg_elem)), ], - var_store, - ), + ret_var: ret_record_var, + }, ), )], final_else: Box::new( // else-branch - no_region( - // Err - tag( - "Err", - vec![tag("OutOfBounds", Vec::new(), var_store)], - var_store, - ), - ), + no_region(record( + vec![("list".into(), list_field), ("value".into(), value_field)], + var_store, + )), ), }; From 81e45b1e2fda559590132f3609a401c42f4ff905 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 27 Feb 2022 22:47:26 +0100 Subject: [PATCH 021/132] make List.set use List.replace under the hood --- compiler/can/src/builtins.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 3e26eb573e..95f198b25d 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -57,6 +57,7 @@ pub fn builtin_dependencies(symbol: Symbol) -> &'static [Symbol] { Symbol::LIST_PRODUCT => &[Symbol::LIST_WALK, Symbol::NUM_MUL], Symbol::LIST_SUM => &[Symbol::LIST_WALK, Symbol::NUM_ADD], Symbol::LIST_JOIN_MAP => &[Symbol::LIST_WALK, Symbol::LIST_CONCAT], + Symbol::LIST_SET => &[Symbol::LIST_REPLACE], _ => &[], } } @@ -2407,6 +2408,23 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { let list_arg_var = var_store.fresh(); // Uniqueness type Attr differs between let list_ret_var = var_store.fresh(); // the arg list and the returned list + let replace_function = ( + var_store.fresh(), + Loc::at_zero(Expr::Var(Symbol::LIST_REPLACE)), + var_store.fresh(), + replace_record_var, + ); + + let replace_call = Expr::Call( + Box::new(replace_function), + vec![ + (list_arg_var, Loc::at_zero(Var(arg_list))), + (len_var, Loc::at_zero(Var(arg_index))), + (elem_var, Loc::at_zero(Var(arg_elem))), + ], + CalledVia::Space, + ); + // Perform a bounds check. If it passes, run LowLevel::ListSet. // Otherwise, return the list unmodified. let body = If { @@ -2439,15 +2457,7 @@ fn list_set(symbol: Symbol, var_store: &mut VarStore) -> Def { field_var: list_ret_var, loc_expr: Box::new(no_region( // List.replaceUnsafe list index elem - RunLowLevel { - op: LowLevel::ListReplaceUnsafe, - args: vec![ - (list_arg_var, Var(arg_list)), - (len_var, Var(arg_index)), - (elem_var, Var(arg_elem)), - ], - ret_var: replace_record_var, - }, + replace_call, )), field: "list".into(), }), From 3db961106e64569ac0f4bb82c429bf61199fee5b Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sun, 27 Feb 2022 14:16:06 -0800 Subject: [PATCH 022/132] Remove now failing tests --- reporting/tests/test_reporting.rs | 118 ------------------------------ 1 file changed, 118 deletions(-) diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 356a123373..f068192ce1 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -4490,32 +4490,6 @@ mod test_reporting { ) } - #[test] - fn record_type_indent_end() { - report_problem_as( - indoc!( - r#" - f : { a: Int - } - "# - ), - indoc!( - r#" - ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── - - I am partway through parsing a record type, but I got stuck here: - - 1│ f : { a: Int - 2│ } - ^ - - I need this curly brace to be indented more. Try adding more spaces - before it! - "# - ), - ) - } - #[test] fn record_type_keyword_field_name() { report_problem_as( @@ -5451,36 +5425,6 @@ mod test_reporting { ) } - #[test] - fn list_bad_indent() { - report_problem_as( - indoc!( - r#" - x = [ 1, 2, - ] - - x - "# - ), - indoc!( - r#" - ── UNFINISHED LIST ───────────────────────────────────────────────────────────── - - I cannot find the end of this list: - - 1│ x = [ 1, 2, - ^ - - You could change it to something like [ 1, 2, 3 ] or even just []. - Anything where there is an open and a close square bracket, and where - the elements of the list are separated by commas. - - Note: I may be confused by indentation - "# - ), - ) - } - #[test] fn number_double_dot() { report_problem_as( @@ -6469,38 +6413,6 @@ I need all branches in an `if` to have the same type! ) } - #[test] - fn outdented_alias() { - report_problem_as( - indoc!( - r#" - Box item : [ - Box item, - Items item item - ] - - 4 - "# - ), - indoc!( - r#" - ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── - - I am partway through parsing a tag union type, but I got stuck here: - - 1│ Box item : [ - 2│ Box item, - 3│ Items item item - 4│ ] - ^ - - I need this square bracket to be indented more. Try adding more spaces - before it! - "# - ), - ) - } - #[test] fn outdented_in_parens() { report_problem_as( @@ -6532,36 +6444,6 @@ I need all branches in an `if` to have the same type! ) } - #[test] - fn outdented_record() { - report_problem_as( - indoc!( - r#" - Box : { - id: Str - } - - 4 - "# - ), - indoc!( - r#" - ── NEED MORE INDENTATION ─────────────────────────────────────────────────────── - - I am partway through parsing a record type, but I got stuck here: - - 1│ Box : { - 2│ id: Str - 3│ } - ^ - - I need this curly brace to be indented more. Try adding more spaces - before it! - "# - ), - ) - } - #[test] fn backpassing_type_error() { report_problem_as( From 2e70bb8458f530979553ca6d44cfd481a033cf59 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 27 Feb 2022 15:07:09 -0800 Subject: [PATCH 023/132] remove list set low level --- compiler/builtins/bitcode/src/list.zig | 91 ------------------------ compiler/builtins/bitcode/src/main.zig | 2 - compiler/builtins/src/bitcode.rs | 2 - compiler/gen_llvm/src/llvm/build.rs | 17 +---- compiler/gen_llvm/src/llvm/build_list.rs | 48 ------------- compiler/gen_wasm/src/low_level.rs | 2 +- compiler/module/src/low_level.rs | 2 - compiler/mono/src/alias_analysis.rs | 8 +-- compiler/mono/src/borrow.rs | 1 - 9 files changed, 5 insertions(+), 168 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 299f4f1591..f9ef75410f 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -1308,97 +1308,6 @@ inline fn listReplaceInPlaceHelp( return list; } -pub fn listSetInPlace( - bytes: ?[*]u8, - index: usize, - element: Opaque, - element_width: usize, - dec: Dec, -) callconv(.C) ?[*]u8 { - // INVARIANT: bounds checking happens on the roc side - // - // at the time of writing, the function is implemented roughly as - // `if inBounds then LowLevelListGet input index item else input` - // so we don't do a bounds check here. Hence, the list is also non-empty, - // because inserting into an empty list is always out of bounds - - return listSetInPlaceHelp(bytes, index, element, element_width, dec); -} - -pub fn listSet( - bytes: ?[*]u8, - length: usize, - alignment: u32, - index: usize, - element: Opaque, - element_width: usize, - dec: Dec, -) callconv(.C) ?[*]u8 { - // INVARIANT: bounds checking happens on the roc side - // - // at the time of writing, the function is implemented roughly as - // `if inBounds then LowLevelListGet input index item else input` - // so we don't do a bounds check here. Hence, the list is also non-empty, - // because inserting into an empty list is always out of bounds - const ptr: [*]usize = @ptrCast([*]usize, @alignCast(@alignOf(usize), bytes)); - - if ((ptr - 1)[0] == utils.REFCOUNT_ONE) { - return listSetInPlaceHelp(bytes, index, element, element_width, dec); - } else { - return listSetImmutable(bytes, length, alignment, index, element, element_width, dec); - } -} - -inline fn listSetInPlaceHelp( - bytes: ?[*]u8, - index: usize, - element: Opaque, - element_width: usize, - dec: Dec, -) ?[*]u8 { - // the element we will replace - var element_at_index = (bytes orelse undefined) + (index * element_width); - - // decrement its refcount - dec(element_at_index); - - // copy in the new element - @memcpy(element_at_index, element orelse undefined, element_width); - - return bytes; -} - -inline fn listSetImmutable( - old_bytes: ?[*]u8, - length: usize, - alignment: u32, - index: usize, - element: Opaque, - element_width: usize, - dec: Dec, -) ?[*]u8 { - const data_bytes = length * element_width; - - var new_bytes = utils.allocateWithRefcount(data_bytes, alignment); - - @memcpy(new_bytes, old_bytes orelse undefined, data_bytes); - - // the element we will replace - var element_at_index = new_bytes + (index * element_width); - - // decrement its refcount - dec(element_at_index); - - // copy in the new element - @memcpy(element_at_index, element orelse undefined, element_width); - - // consume RC token of original - utils.decref(old_bytes, data_bytes, alignment); - - //return list; - return new_bytes; -} - pub fn listFindUnsafe( list: RocList, caller: Caller1, diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index 8a23f09985..5388804416 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -51,8 +51,6 @@ comptime { exportListFn(list.listDropAt, "drop_at"); exportListFn(list.listReplace, "replace"); exportListFn(list.listReplaceInPlace, "replace_in_place"); - exportListFn(list.listSet, "set"); - exportListFn(list.listSetInPlace, "set_in_place"); exportListFn(list.listSwap, "swap"); exportListFn(list.listAny, "any"); exportListFn(list.listAll, "all"); diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index c4c0b12e01..5be3ecb830 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -356,8 +356,6 @@ pub const LIST_SORT_WITH: &str = "roc_builtins.list.sort_with"; pub const LIST_CONCAT: &str = "roc_builtins.list.concat"; pub const LIST_REPLACE: &str = "roc_builtins.list.replace"; pub const LIST_REPLACE_IN_PLACE: &str = "roc_builtins.list.replace_in_place"; -pub const LIST_SET: &str = "roc_builtins.list.set"; -pub const LIST_SET_IN_PLACE: &str = "roc_builtins.list.set_in_place"; pub const LIST_ANY: &str = "roc_builtins.list.any"; pub const LIST_ALL: &str = "roc_builtins.list.all"; pub const LIST_FIND_UNSAFE: &str = "roc_builtins.list.find_unsafe"; diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index d3be9df353..a80b1becb4 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -14,7 +14,7 @@ use crate::llvm::build_list::{ list_contains, list_drop_at, list_find_unsafe, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map4, list_map_with_index, list_prepend, list_range, list_repeat, list_replace_unsafe, list_reverse, - list_set, list_single, list_sort_with, list_sublist, list_swap, + list_single, list_sort_with, list_sublist, list_swap, }; use crate::llvm::build_str::{ str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8, @@ -5681,21 +5681,6 @@ fn run_low_level<'a, 'ctx, 'env>( update_mode, ) } - ListSet => { - let list = load_symbol(scope, &args[0]); - let index = load_symbol(scope, &args[1]); - let (element, element_layout) = load_symbol_and_layout(scope, &args[2]); - - list_set( - env, - layout_ids, - list, - index.into_int_value(), - element, - element_layout, - update_mode, - ) - } NumToStr => { // Num.toStr : Num a -> Str debug_assert_eq!(args.len(), 1); diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index ede3e6430a..c818ed4d23 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -360,54 +360,6 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( .into() } -/// List.set : List elem, Nat, elem -> List elem -pub fn list_set<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - list: BasicValueEnum<'ctx>, - index: IntValue<'ctx>, - element: BasicValueEnum<'ctx>, - element_layout: &Layout<'a>, - update_mode: UpdateMode, -) -> BasicValueEnum<'ctx> { - let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); - - let (length, bytes) = load_list( - env.builder, - list.into_struct_value(), - env.context.i8_type().ptr_type(AddressSpace::Generic), - ); - - let new_bytes = match update_mode { - UpdateMode::InPlace => call_bitcode_fn( - env, - &[ - bytes.into(), - index.into(), - pass_element_as_opaque(env, element, *element_layout), - layout_width(env, element_layout), - dec_element_fn.as_global_value().as_pointer_value().into(), - ], - bitcode::LIST_SET_IN_PLACE, - ), - UpdateMode::Immutable => call_bitcode_fn( - env, - &[ - bytes.into(), - length.into(), - env.alignment_intvalue(element_layout), - index.into(), - pass_element_as_opaque(env, element, *element_layout), - layout_width(env, element_layout), - dec_element_fn.as_global_value().as_pointer_value().into(), - ], - bitcode::LIST_SET, - ), - }; - - store_list(env, new_bytes.into_pointer_value(), length) -} - fn bounds_check_comparison<'ctx>( builder: &Builder<'ctx>, elem_index: IntValue<'ctx>, diff --git a/compiler/gen_wasm/src/low_level.rs b/compiler/gen_wasm/src/low_level.rs index b68d8e968a..f22ebd0bbb 100644 --- a/compiler/gen_wasm/src/low_level.rs +++ b/compiler/gen_wasm/src/low_level.rs @@ -260,7 +260,7 @@ impl<'a> LowLevelCall<'a> { _ => internal_error!("invalid storage for List"), }, - ListGetUnsafe | ListReplaceUnsafe | ListSet | ListSingle | ListRepeat | ListReverse + ListGetUnsafe | ListReplaceUnsafe | ListSingle | ListRepeat | ListReverse | ListConcat | ListContains | ListAppend | ListPrepend | ListJoin | ListRange | ListMap | ListMap2 | ListMap3 | ListMap4 | ListMapWithIndex | ListKeepIf | ListWalk | ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index ad10a1cbba..f7b3987be1 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -25,7 +25,6 @@ pub enum LowLevel { StrToNum, ListLen, ListGetUnsafe, - ListSet, ListSingle, ListRepeat, ListReplaceUnsafe, @@ -229,7 +228,6 @@ impl LowLevelWrapperType { Symbol::STR_TO_I8 => WrapperIsRequired, Symbol::LIST_LEN => CanBeReplacedBy(ListLen), Symbol::LIST_GET => WrapperIsRequired, - Symbol::LIST_SET => WrapperIsRequired, Symbol::LIST_REPLACE => WrapperIsRequired, Symbol::LIST_SINGLE => CanBeReplacedBy(ListSingle), Symbol::LIST_REPEAT => CanBeReplacedBy(ListRepeat), diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index f25e3e3b08..f104a2f20a 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -1258,19 +1258,17 @@ fn lowlevel_spec( builder.add_bag_get(block, bag) } - ListSet => { + ListReplaceUnsafe => { let list = env.symbols[&arguments[0]]; let to_insert = env.symbols[&arguments[2]]; let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; - // decrement the overwritten element - let overwritten = builder.add_bag_get(block, bag)?; - let _unit = builder.add_recursive_touch(block, overwritten)?; - let _unit = builder.add_update(block, update_mode_var, cell)?; + // replace loads the old element and then inserts the new one. + builder.add_bag_get(block, bag)?; builder.add_bag_insert(block, bag, to_insert)?; with_new_heap_cell(builder, block, bag) diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index d46ccbc8ee..2d081fb7b4 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -934,7 +934,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { // - other refcounted arguments are Borrowed match op { ListLen | StrIsEmpty | StrCountGraphemes => arena.alloc_slice_copy(&[borrowed]), - ListSet => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListConcat => arena.alloc_slice_copy(&[owned, owned]), From 457ba524aacda0d3ca08bde34623d0f057dcf822 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 27 Feb 2022 15:20:54 -0800 Subject: [PATCH 024/132] fix tests and alias analysis --- compiler/mono/src/alias_analysis.rs | 9 +++++---- compiler/test_gen/src/gen_list.rs | 31 ++++++++++------------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index f104a2f20a..db061cf395 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -1265,13 +1265,14 @@ fn lowlevel_spec( let bag = builder.add_get_tuple_field(block, list, LIST_BAG_INDEX)?; let cell = builder.add_get_tuple_field(block, list, LIST_CELL_INDEX)?; - let _unit = builder.add_update(block, update_mode_var, cell)?; + let _unit1 = builder.add_touch(block, cell)?; + let _unit2 = builder.add_update(block, update_mode_var, cell)?; - // replace loads the old element and then inserts the new one. - builder.add_bag_get(block, bag)?; builder.add_bag_insert(block, bag, to_insert)?; - with_new_heap_cell(builder, block, bag) + let old_value = builder.add_bag_get(block, bag)?; + let new_list = with_new_heap_cell(builder, block, bag)?; + builder.add_make_tuple(block, &[new_list, old_value]) } ListSwap => { let list = env.symbols[&arguments[0]]; diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index ac34090e7d..8eb784c2b8 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1769,10 +1769,8 @@ fn replace_unique_int_list() { assert_evals_to!( indoc!( r#" - result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 - when result is - Ok {list} -> list - Err _ -> [] + record = List.replace [ 12, 9, 7, 1, 5 ] 2 33 + record.list "# ), RocList::from_slice(&[12, 9, 33, 1, 5]), @@ -1786,13 +1784,11 @@ fn replace_unique_int_list_out_of_bounds() { assert_evals_to!( indoc!( r#" - result = List.replace [ 12, 9, 7, 1, 5 ] 5 33 - when result is - Ok {value} -> value - Err OutOfBounds -> -1 + record = List.replace [ 12, 9, 7, 1, 5 ] 5 33 + record.value "# ), - -1, + 33, i64 ); } @@ -1803,10 +1799,8 @@ fn replace_unique_int_list_get_old_value() { assert_evals_to!( indoc!( r#" - result = List.replace [ 12, 9, 7, 1, 5 ] 2 33 - when result is - Ok {value} -> value - Err _ -> -1 + record = List.replace [ 12, 9, 7, 1, 5 ] 2 33 + record.value "# ), 7, @@ -1822,10 +1816,8 @@ fn replace_unique_get_large_value() { r#" list : List { a : U64, b: U64, c: U64, d: U64 } list = [ { a: 1, b: 2, c: 3, d: 4 }, { a: 5, b: 6, c: 7, d: 8 }, { a: 9, b: 10, c: 11, d: 12 } ] - result = List.replace list 1 { a: 13, b: 14, c: 15, d: 16 } - when result is - Ok {value} -> value - Err _ -> { a: 0, b: 0, c: 0, d: 0 } + record = List.replace list 1 { a: 13, b: 14, c: 15, d: 16 } + record.value "# ), (5, 6, 7, 8), @@ -1841,10 +1833,7 @@ fn replace_shared_int_list() { r#" wrapper = \shared -> # This should not mutate the original - replaced = - when List.replace shared 1 7.7 is - Ok {list} -> list - Err _ -> [] + replaced = (List.replace shared 1 7.7).list x = when List.get replaced 1 is Ok num -> num From 2ca1ebdd2d380a1676346d75c15a4a104a4c3d2c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 27 Feb 2022 15:50:19 -0800 Subject: [PATCH 025/132] appease the paperclip --- compiler/builtins/src/std.rs | 2 +- compiler/gen_llvm/src/llvm/build_list.rs | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index d9044f4f6e..8c6e96bbd4 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -1034,7 +1034,7 @@ pub fn types() -> MutMap { add_top_level_function_type!( Symbol::LIST_GET, vec![list_type(flex(TVAR1)), nat_type()], - Box::new(result_type(flex(TVAR1), index_out_of_bounds.clone())), + Box::new(result_type(flex(TVAR1), index_out_of_bounds)), ); // first : List elem -> Result elem [ ListWasEmpty ]* diff --git a/compiler/gen_llvm/src/llvm/build_list.rs b/compiler/gen_llvm/src/llvm/build_list.rs index c818ed4d23..dca6e49466 100644 --- a/compiler/gen_llvm/src/llvm/build_list.rs +++ b/compiler/gen_llvm/src/llvm/build_list.rs @@ -301,7 +301,7 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( element_layout: &Layout<'a>, update_mode: UpdateMode, ) -> BasicValueEnum<'ctx> { - let element_type = basic_type_from_layout(env, &element_layout); + let element_type = basic_type_from_layout(env, element_layout); let element_ptr = env .builder .build_alloca(element_type, "output_element_as_opaque"); @@ -340,10 +340,7 @@ pub fn list_replace_unsafe<'a, 'ctx, 'env>( let result = env .context .struct_type( - &[ - super::convert::zig_list_type(env).into(), - element_type.into(), - ], + &[super::convert::zig_list_type(env).into(), element_type], false, ) .const_zero(); From 784894bb8fb5a6a11b0b19fce75fe2b1d94ce311 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 28 Feb 2022 13:48:11 +0100 Subject: [PATCH 026/132] fixing windows cargo build errors --- Cargo.lock | 14 +++++----- ast/Cargo.toml | 8 +++++- ast/src/mem_pool/pool.rs | 54 ++++++++++++++++++++++++++++---------- compiler/builtins/build.rs | 7 ++++- linker/Cargo.toml | 2 +- linker/src/lib.rs | 12 ++++++--- repl_cli/Cargo.toml | 4 +-- 7 files changed, 72 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69d526c6f6..0a3d8e388b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1162,7 +1162,7 @@ checksum = "1d428afc93ad288f6dffc1fa5f4a78201ad2eec33c5a522e51c181009eb09061" dependencies = [ "byteorder", "dynasm", - "memmap2 0.5.0", + "memmap2 0.5.3", ] [[package]] @@ -2018,9 +2018,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.5.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4647a11b578fead29cdbb34d4adef8dd3dc35b876c9c6d5240d83f205abfe96e" +checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" dependencies = [ "libc", ] @@ -3195,8 +3195,10 @@ dependencies = [ "roc_target", "roc_types", "roc_unify", + "slab", "snafu", "ven_graph", + "winapi", ] [[package]] @@ -3495,7 +3497,7 @@ dependencies = [ "bumpalo", "clap 3.0.0-beta.5", "iced-x86", - "memmap2 0.5.0", + "memmap2 0.5.3", "object 0.26.2", "roc_build", "roc_collections", @@ -3825,7 +3827,7 @@ checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" [[package]] name = "rustyline" version = "9.1.1" -source = "git+https://github.com/rtfeldman/rustyline?tag=v9.1.1#7053ae0fe0ee710d38ed5845dd979113382994dc" +source = "git+https://github.com/rtfeldman/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -3848,7 +3850,7 @@ dependencies = [ [[package]] name = "rustyline-derive" version = "0.6.0" -source = "git+https://github.com/rtfeldman/rustyline?tag=v9.1.1#7053ae0fe0ee710d38ed5845dd979113382994dc" +source = "git+https://github.com/rtfeldman/rustyline?rev=e74333c#e74333c0d618896b88175bf06645108f996fe6d0" dependencies = [ "quote", "syn", diff --git a/ast/Cargo.toml b/ast/Cargo.toml index f0b82d7ca4..945f1f660e 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -21,10 +21,16 @@ roc_target = { path = "../compiler/roc_target" } roc_error_macros = { path = "../error_macros" } arrayvec = "0.7.2" bumpalo = { version = "3.8.0", features = ["collections"] } -libc = "0.2.106" page_size = "0.4.2" snafu = { version = "0.6.10", features = ["backtraces"] } ven_graph = { path = "../vendor/pathfinding" } +slab = "0.4.5" [dev-dependencies] indoc = "1.0.3" + +[target.'cfg(windows)'.dependencies] +winapi = "0.3.9" + +[target.'cfg(unix)'.dependencies] +libc = "0.2.106" diff --git a/ast/src/mem_pool/pool.rs b/ast/src/mem_pool/pool.rs index 6641627c76..ec55b04315 100644 --- a/ast/src/mem_pool/pool.rs +++ b/ast/src/mem_pool/pool.rs @@ -10,12 +10,10 @@ /// /// Pages also use the node value 0 (all 0 bits) to mark nodes as unoccupied. /// This is important for performance. -use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE}; use std::any::type_name; use std::ffi::c_void; use std::marker::PhantomData; use std::mem::{align_of, size_of, MaybeUninit}; -use std::ptr::null; pub const NODE_BYTES: usize = 32; @@ -108,14 +106,30 @@ impl Pool { // addresses from the OS which will be lazily translated into // physical memory one 4096-byte page at a time, once we actually // try to read or write in that page's address range. - libc::mmap( - null::() as *mut c_void, - bytes_to_mmap, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - 0, - 0, - ) + #[cfg(unix)]{ + use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE}; + + libc::mmap( + std::ptr::null_mut(), + bytes_to_mmap, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + 0, + 0, + ) + } + #[cfg(windows)]{ + use winapi::um::memoryapi::{VirtualAlloc}; + use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE}; + use winapi::um::winnt::{PAGE_READWRITE}; + + VirtualAlloc( + std::ptr::null_mut(), + bytes_to_mmap, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ) + } } as *mut [MaybeUninit; NODE_BYTES]; // This is our actual capacity, in nodes. @@ -230,10 +244,22 @@ impl std::ops::IndexMut> for Pool { impl Drop for Pool { fn drop(&mut self) { unsafe { - libc::munmap( - self.nodes as *mut c_void, - NODE_BYTES * self.capacity as usize, - ); + #[cfg(unix)]{ + libc::munmap( + self.nodes as *mut c_void, + NODE_BYTES * self.capacity as usize, + ); + } + #[cfg(windows)]{ + use winapi::um::memoryapi::{VirtualFree}; + use winapi::um::winnt::{MEM_RELEASE}; + + VirtualFree( + self.nodes as *mut c_void, + NODE_BYTES * self.capacity as usize, + MEM_RELEASE + ); + } } } } diff --git a/compiler/builtins/build.rs b/compiler/builtins/build.rs index b117f888f7..90ee7c8d96 100644 --- a/compiler/builtins/build.rs +++ b/compiler/builtins/build.rs @@ -50,12 +50,17 @@ fn main() { ); // OBJECT FILES + #[cfg(windows)] + const BUILTINS_HOST_FILE: &str = "builtins-host.obj"; + + #[cfg(not(windows))] + const BUILTINS_HOST_FILE: &str = "builtins-host.o"; generate_object_file( &bitcode_path, "BUILTINS_HOST_O", "object", - "builtins-host.o", + BUILTINS_HOST_FILE, ); generate_object_file( diff --git a/linker/Cargo.toml b/linker/Cargo.toml index 39cfdcdd37..697771fafa 100644 --- a/linker/Cargo.toml +++ b/linker/Cargo.toml @@ -24,7 +24,7 @@ roc_collections = { path = "../compiler/collections" } bumpalo = { version = "3.8.0", features = ["collections"] } clap = { version = "= 3.0.0-beta.5", default-features = false, features = ["std", "color", "suggestions"] } iced-x86 = { version = "1.15.0", default-features = false, features = ["std", "decoder", "op_code_info", "instr_info"] } -memmap2 = "0.5.0" +memmap2 = "0.5.3" object = { version = "0.26.2", features = ["read", "write"] } serde = { version = "1.0.130", features = ["derive"] } bincode = "1.3.3" diff --git a/linker/src/lib.rs b/linker/src/lib.rs index b9d6ee8519..e7abf8fe35 100644 --- a/linker/src/lib.rs +++ b/linker/src/lib.rs @@ -20,7 +20,6 @@ use std::io; use std::io::{BufReader, BufWriter}; use std::mem; use std::os::raw::c_char; -use std::os::unix::fs::PermissionsExt; use std::path::Path; use std::process::Command; use std::time::{Duration, SystemTime}; @@ -1627,9 +1626,14 @@ fn surgery_impl( let flushing_data_duration = flushing_data_start.elapsed().unwrap(); // Make sure the final executable has permision to execute. - let mut perms = fs::metadata(out_filename)?.permissions(); - perms.set_mode(perms.mode() | 0o111); - fs::set_permissions(out_filename, perms)?; + // TODO windows alternative? + #[cfg(target_family = "unix")] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(out_filename)?.permissions(); + perms.set_mode(perms.mode() | 0o111); + fs::set_permissions(out_filename, perms)?; + } let total_duration = total_start.elapsed().unwrap(); diff --git a/repl_cli/Cargo.toml b/repl_cli/Cargo.toml index 240d7113ba..dd5c3ee3c6 100644 --- a/repl_cli/Cargo.toml +++ b/repl_cli/Cargo.toml @@ -10,8 +10,8 @@ bumpalo = {version = "3.8.0", features = ["collections"]} const_format = "0.2.22" inkwell = {path = "../vendor/inkwell"} libloading = {version = "0.7.1"} -rustyline = {git = "https://github.com/rtfeldman/rustyline", tag = "v9.1.1"} -rustyline-derive = {git = "https://github.com/rtfeldman/rustyline", tag = "v9.1.1"} +rustyline = {git = "https://github.com/rtfeldman/rustyline", rev = "e74333c"} +rustyline-derive = {git = "https://github.com/rtfeldman/rustyline", rev = "e74333c"} target-lexicon = "0.12.2" roc_build = {path = "../compiler/build"} From 6acd2f338b058523a8fff97fc5e1850feacb3e29 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 28 Feb 2022 18:11:14 +0100 Subject: [PATCH 027/132] successful cargo build on windows --- BUILDING_FROM_SOURCE.md | 26 +++++++++++++---------- ast/Cargo.toml | 6 +++--- cli_utils/Cargo.toml | 2 ++ cli_utils/src/bench_utils.rs | 41 ++++++++++++++++++++++++------------ compiler/build/src/link.rs | 13 ++++++++++++ compiler/build/src/target.rs | 5 +++++ compiler/builtins/build.rs | 3 ++- getting_started/windows.md | 2 +- roc_std/src/lib.rs | 18 ++++++++++++++++ 9 files changed, 86 insertions(+), 30 deletions(-) diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index 7bae697da1..791867b6a1 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -197,20 +197,24 @@ export CPPFLAGS="-I/usr/local/opt/llvm/include" ### LLVM installation on Windows -Installing LLVM's prebuilt binaries doesn't seem to be enough for the `llvm-sys` crate that Roc depends on, so I had to build LLVM from source -on Windows. After lots of help from [**@IanMacKenzie**](https://github.com/IanMacKenzie) (thank you, Ian!), here's what worked for me: +**Warning** While `cargo build` works on windows, linking roc programs does not yet, see issue #2608. This also means the repl, the editor and many tests will not work on windows. +Installing LLVM's prebuilt binaries doesn't seem to be enough for the `llvm-sys` crate that Roc depends on, so I had to follow the steps below: -1. I downloaded and installed [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (a full Visual Studio install should work tool; the Build Tools are just the CLI tools, which is all I wanted) -1. In the installation configuration, under "additional components" I had to check both "C++ ATL for latest v142 build tools (x86 & x64)" and also "C++/CLI support for v142 build tools" [note: as of September 2021 this should no longer be necessary - the next time anyone tries this, please try it without this step and make a PR to delete this step if it's no longer needed!] -1. I launched the "x64 Native Tools Command Prompt for Visual Studio 2019" application (note: not the similarly-named "x86" one!) -1. Make sure [Python 2.7](https://www.python.org/) and [CMake 3.17](http://cmake.org/) are installed on your system. -1. I followed most of the steps under LLVM's [building from source instructions](https://github.com/llvm/llvm-project#getting-the-source-code-and-building-llvm) up to the `cmake -G ...` command, which didn't work for me. Instead, at that point I did the following step. -1. I ran `cmake -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release ../llvm` to generate a NMake makefile. -1. Once that completed, I ran `nmake` to build LLVM. (This took about 2 hours on my laptop.) -1. Finally, I set an environment variable `LLVM_SYS_100_PREFIX` to point to the `build` directory where I ran the `cmake` command. +1. I downloaded and installed [Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (a full Visual Studio install should work too; the Build Tools are just the CLI tools, which is all I wanted) +1. Download the custom LLVM 7z archive [here](https://github.com/PLC-lang/llvm-package-windows/releases/tag/v12.0.1). +1. [Download 7-zip](https://www.7-zip.org/) to be able to extract this archive. +1. Extract the 7z file to where you want to permanently keep the folder. +1. In powershell, set the `LLVM_SYS_120_PREFIX` environment variable: +``` +[Environment]::SetEnvironmentVariable( + "Path", + [Environment]::GetEnvironmentVariable("Path", "User") + ";C:\Users\anton\Downloads\LLVM-12.0.1-win64\bin", + "User" +) +``` -Once all that was done, `cargo` ran successfully for Roc! +Once all that was done, `cargo build` ran successfully for Roc! ### Build speed on WSL/WSL2 diff --git a/ast/Cargo.toml b/ast/Cargo.toml index 945f1f660e..b27cdbbf62 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -25,12 +25,12 @@ page_size = "0.4.2" snafu = { version = "0.6.10", features = ["backtraces"] } ven_graph = { path = "../vendor/pathfinding" } slab = "0.4.5" +libc = "0.2.106" [dev-dependencies] indoc = "1.0.3" [target.'cfg(windows)'.dependencies] -winapi = "0.3.9" +winapi = { version = "0.3.9", features = ["memoryapi", "heapapi", "synchapi", "winbase", "sysinfoapi"]} +# TODO I'm not sure if all these features are necessary -[target.'cfg(unix)'.dependencies] -libc = "0.2.106" diff --git a/cli_utils/Cargo.toml b/cli_utils/Cargo.toml index a39dadba80..6f90db6737 100644 --- a/cli_utils/Cargo.toml +++ b/cli_utils/Cargo.toml @@ -20,4 +20,6 @@ serde = { version = "1.0.130", features = ["derive"] } serde-xml-rs = "0.5.1" strip-ansi-escapes = "0.1.1" tempfile = "3.2.0" + +[target.'cfg(unix)'.dependencies] rlimit = "0.6.2" diff --git a/cli_utils/src/bench_utils.rs b/cli_utils/src/bench_utils.rs index b01ec495fd..762970052e 100644 --- a/cli_utils/src/bench_utils.rs +++ b/cli_utils/src/bench_utils.rs @@ -1,11 +1,12 @@ use crate::helpers::{example_file, run_cmd, run_roc}; use criterion::{black_box, measurement::Measurement, BenchmarkGroup}; -use rlimit::{setrlimit, Resource}; -use std::path::Path; +use std::{path::Path, thread}; + +const CFOLD_STACK_SIZE: usize = 8192 * 100000; fn exec_bench_w_input( file: &Path, - stdin_str: &str, + stdin_str: &'static str, executable_filename: &str, expected_ending: &str, bench_group_opt: Option<&mut BenchmarkGroup>, @@ -31,7 +32,7 @@ fn exec_bench_w_input( fn check_cmd_output( file: &Path, - stdin_str: &str, + stdin_str: &'static str, executable_filename: &str, expected_ending: &str, ) { @@ -41,11 +42,19 @@ fn check_cmd_output( .unwrap() .to_string(); - if cmd_str.contains("cfold") { - increase_stack_limit(); - } + let out= if cmd_str.contains("cfold") { + let child = thread::Builder::new() + .stack_size(CFOLD_STACK_SIZE) + .spawn( + move|| {run_cmd(&cmd_str, &[stdin_str], &[])} + ) + .unwrap(); + + child.join().unwrap() + } else { + run_cmd(&cmd_str, &[stdin_str], &[]) + }; - let out = run_cmd(&cmd_str, &[stdin_str], &[]); if !&out.stdout.ends_with(expected_ending) { panic!( @@ -69,7 +78,16 @@ fn bench_cmd( .to_string(); if cmd_str.contains("cfold") { - increase_stack_limit(); + #[cfg(unix)] + use rlimit::{setrlimit, Resource}; + #[cfg(unix)] + setrlimit(Resource::STACK, CFOLD_STACK_SIZE, CFOLD_STACK_SIZE) + .expect("Failed to increase stack limit."); + + #[cfg(windows)] + println!("Skipping the cfold benchmark on windows, I can't adjust the stack size and use criterion at the same time."); + #[cfg(windows)] + return; } if let Some(bench_group) = bench_group_opt { @@ -85,11 +103,6 @@ fn bench_cmd( } } -fn increase_stack_limit() { - let new_stack_limit = 8192 * 100000; - setrlimit(Resource::STACK, new_stack_limit, new_stack_limit) - .expect("Failed to increase stack limit."); -} pub fn bench_nqueens(bench_group_opt: Option<&mut BenchmarkGroup>) { exec_bench_w_input( diff --git a/compiler/build/src/link.rs b/compiler/build/src/link.rs index eab0872161..ff9b928145 100644 --- a/compiler/build/src/link.rs +++ b/compiler/build/src/link.rs @@ -46,6 +46,10 @@ pub fn link( operating_system: OperatingSystem::Darwin, .. } => link_macos(target, output_path, input_paths, link_type), + Triple { + operating_system: OperatingSystem::Windows, + .. + } => link_windows(target, output_path, input_paths, link_type), _ => panic!("TODO gracefully handle unsupported target: {:?}", target), } } @@ -965,6 +969,15 @@ fn link_wasm32( Ok((child, output_path)) } +fn link_windows( + _target: &Triple, + _output_path: PathBuf, + _input_paths: &[&str], + _link_type: LinkType, +) -> io::Result<(Child, PathBuf)> { + todo!("Add windows support to the surgical linker. See issue #2608.") +} + #[cfg(feature = "llvm")] pub fn module_to_dylib( module: &inkwell::module::Module, diff --git a/compiler/build/src/target.rs b/compiler/build/src/target.rs index 6064e58428..be854c74bb 100644 --- a/compiler/build/src/target.rs +++ b/compiler/build/src/target.rs @@ -41,6 +41,11 @@ pub fn target_triple_str(target: &Triple) -> &'static str { operating_system: OperatingSystem::Darwin, .. } => "x86_64-unknown-darwin10", + Triple { + architecture: Architecture::X86_64, + operating_system: OperatingSystem::Windows, + .. + }=> "x86_64-pc-windows-gnu", _ => panic!("TODO gracefully handle unsupported target: {:?}", target), } } diff --git a/compiler/builtins/build.rs b/compiler/builtins/build.rs index 90ee7c8d96..1e389b5b56 100644 --- a/compiler/builtins/build.rs +++ b/compiler/builtins/build.rs @@ -109,7 +109,8 @@ fn generate_object_file( println!("Moving zig object `{}` to: {}", zig_object, dest_obj); // we store this .o file in rust's `target` folder - run_command(&bitcode_path, "mv", &[src_obj, dest_obj]); + fs::copy(src_obj, dest_obj).expect("Failed to copy object file."); + fs::remove_file(src_obj).expect("Failed to remove original object file after copy."); } fn generate_bc_file( diff --git a/getting_started/windows.md b/getting_started/windows.md index ae0556833d..de3a7e1cbc 100644 --- a/getting_started/windows.md +++ b/getting_started/windows.md @@ -1,2 +1,2 @@ -Windows is not yet supported, we have a big project in the works that will make it easier to achieve this. +Windows is not yet supported, we have a big project in the works (see issue #2608) that will allow this. Until then we recommend using Ubuntu through the "Windows Subsystem for Linux". \ No newline at end of file diff --git a/roc_std/src/lib.rs b/roc_std/src/lib.rs index 4eecf04adb..b4c4066457 100644 --- a/roc_std/src/lib.rs +++ b/roc_std/src/lib.rs @@ -12,7 +12,9 @@ mod roc_str; pub use roc_list::RocList; pub use roc_str::RocStr; + // A list of C functions that are being imported +#[cfg(not(windows))] extern "C" { pub fn roc_alloc(size: usize, alignment: u32) -> *mut c_void; pub fn roc_realloc( @@ -24,6 +26,22 @@ extern "C" { pub fn roc_dealloc(ptr: *mut c_void, alignment: u32); } +#[cfg(windows)] +const ERR_MSG: &str = "should not be called from within the repo. If you got this while running a roc app, the linker should have filled this function in, but it did not happen."; +#[cfg(windows)] +pub fn roc_alloc(_size: usize, _alignment: u32) -> *mut c_void {panic!("roc_alloc {}", ERR_MSG)} +#[cfg(windows)] +pub fn roc_realloc( + _ptr: *mut c_void, + _new_size: usize, + _old_size: usize, + _alignment: u32, +) -> *mut c_void {panic!("roc_realloc {}", ERR_MSG)} +#[cfg(windows)] +pub fn roc_dealloc(_ptr: *mut c_void, _alignment: u32) {panic!("roc_dealloc {}", ERR_MSG)} + + + const REFCOUNT_1: isize = isize::MIN; #[repr(u8)] From 5f865a4a0c6c6887c6750868763a1fc01a701339 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 28 Feb 2022 18:18:41 +0100 Subject: [PATCH 028/132] fmt --- ast/src/mem_pool/pool.rs | 22 +++++++++++++--------- cli_utils/src/bench_utils.rs | 8 ++------ compiler/build/src/target.rs | 2 +- compiler/can/src/annotation.rs | 3 +-- linker/src/lib.rs | 24 ++++++------------------ roc_std/src/lib.rs | 15 +++++++++------ 6 files changed, 32 insertions(+), 42 deletions(-) diff --git a/ast/src/mem_pool/pool.rs b/ast/src/mem_pool/pool.rs index ec55b04315..e28dbc33bd 100644 --- a/ast/src/mem_pool/pool.rs +++ b/ast/src/mem_pool/pool.rs @@ -106,7 +106,8 @@ impl Pool { // addresses from the OS which will be lazily translated into // physical memory one 4096-byte page at a time, once we actually // try to read or write in that page's address range. - #[cfg(unix)]{ + #[cfg(unix)] + { use libc::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE}; libc::mmap( @@ -118,10 +119,11 @@ impl Pool { 0, ) } - #[cfg(windows)]{ - use winapi::um::memoryapi::{VirtualAlloc}; + #[cfg(windows)] + { + use winapi::um::memoryapi::VirtualAlloc; + use winapi::um::winnt::PAGE_READWRITE; use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE}; - use winapi::um::winnt::{PAGE_READWRITE}; VirtualAlloc( std::ptr::null_mut(), @@ -244,20 +246,22 @@ impl std::ops::IndexMut> for Pool { impl Drop for Pool { fn drop(&mut self) { unsafe { - #[cfg(unix)]{ + #[cfg(unix)] + { libc::munmap( self.nodes as *mut c_void, NODE_BYTES * self.capacity as usize, ); } - #[cfg(windows)]{ - use winapi::um::memoryapi::{VirtualFree}; - use winapi::um::winnt::{MEM_RELEASE}; + #[cfg(windows)] + { + use winapi::um::memoryapi::VirtualFree; + use winapi::um::winnt::MEM_RELEASE; VirtualFree( self.nodes as *mut c_void, NODE_BYTES * self.capacity as usize, - MEM_RELEASE + MEM_RELEASE, ); } } diff --git a/cli_utils/src/bench_utils.rs b/cli_utils/src/bench_utils.rs index 762970052e..bb46698602 100644 --- a/cli_utils/src/bench_utils.rs +++ b/cli_utils/src/bench_utils.rs @@ -42,12 +42,10 @@ fn check_cmd_output( .unwrap() .to_string(); - let out= if cmd_str.contains("cfold") { + let out = if cmd_str.contains("cfold") { let child = thread::Builder::new() .stack_size(CFOLD_STACK_SIZE) - .spawn( - move|| {run_cmd(&cmd_str, &[stdin_str], &[])} - ) + .spawn(move || run_cmd(&cmd_str, &[stdin_str], &[])) .unwrap(); child.join().unwrap() @@ -55,7 +53,6 @@ fn check_cmd_output( run_cmd(&cmd_str, &[stdin_str], &[]) }; - if !&out.stdout.ends_with(expected_ending) { panic!( "expected output to end with {:?} but instead got {:#?}", @@ -103,7 +100,6 @@ fn bench_cmd( } } - pub fn bench_nqueens(bench_group_opt: Option<&mut BenchmarkGroup>) { exec_bench_w_input( &example_file("benchmarks", "NQueens.roc"), diff --git a/compiler/build/src/target.rs b/compiler/build/src/target.rs index be854c74bb..92dfed1c8e 100644 --- a/compiler/build/src/target.rs +++ b/compiler/build/src/target.rs @@ -45,7 +45,7 @@ pub fn target_triple_str(target: &Triple) -> &'static str { architecture: Architecture::X86_64, operating_system: OperatingSystem::Windows, .. - }=> "x86_64-pc-windows-gnu", + } => "x86_64-pc-windows-gnu", _ => panic!("TODO gracefully handle unsupported target: {:?}", target), } } diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 8d3febe818..a2a32e3120 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -383,8 +383,7 @@ fn can_annotation_help( As( loc_inner, _spaces, - alias_header - @ TypeHeader { + alias_header @ TypeHeader { name, vars: loc_vars, }, diff --git a/linker/src/lib.rs b/linker/src/lib.rs index e7abf8fe35..9dc6d76caa 100644 --- a/linker/src/lib.rs +++ b/linker/src/lib.rs @@ -366,9 +366,7 @@ fn preprocess_impl( Some(section) => { let file_offset = match section.compressed_file_range() { Ok( - range - @ - CompressedFileRange { + range @ CompressedFileRange { format: CompressionFormat::None, .. }, @@ -493,9 +491,7 @@ fn preprocess_impl( for sec in text_sections { let (file_offset, compressed) = match sec.compressed_file_range() { Ok( - range - @ - CompressedFileRange { + range @ CompressedFileRange { format: CompressionFormat::None, .. }, @@ -625,9 +621,7 @@ fn preprocess_impl( }; let dyn_offset = match dyn_sec.compressed_file_range() { Ok( - range - @ - CompressedFileRange { + range @ CompressedFileRange { format: CompressionFormat::None, .. }, @@ -713,9 +707,7 @@ fn preprocess_impl( }; let symtab_offset = match symtab_sec.compressed_file_range() { Ok( - range - @ - CompressedFileRange { + range @ CompressedFileRange { format: CompressionFormat::None, .. }, @@ -737,9 +729,7 @@ fn preprocess_impl( }; let dynsym_offset = match dynsym_sec.compressed_file_range() { Ok( - range - @ - CompressedFileRange { + range @ CompressedFileRange { format: CompressionFormat::None, .. }, @@ -758,9 +748,7 @@ fn preprocess_impl( { match sec.compressed_file_range() { Ok( - range - @ - CompressedFileRange { + range @ CompressedFileRange { format: CompressionFormat::None, .. }, diff --git a/roc_std/src/lib.rs b/roc_std/src/lib.rs index b4c4066457..98e70384b1 100644 --- a/roc_std/src/lib.rs +++ b/roc_std/src/lib.rs @@ -12,7 +12,6 @@ mod roc_str; pub use roc_list::RocList; pub use roc_str::RocStr; - // A list of C functions that are being imported #[cfg(not(windows))] extern "C" { @@ -29,18 +28,22 @@ extern "C" { #[cfg(windows)] const ERR_MSG: &str = "should not be called from within the repo. If you got this while running a roc app, the linker should have filled this function in, but it did not happen."; #[cfg(windows)] -pub fn roc_alloc(_size: usize, _alignment: u32) -> *mut c_void {panic!("roc_alloc {}", ERR_MSG)} +pub fn roc_alloc(_size: usize, _alignment: u32) -> *mut c_void { + panic!("roc_alloc {}", ERR_MSG) +} #[cfg(windows)] pub fn roc_realloc( _ptr: *mut c_void, _new_size: usize, _old_size: usize, _alignment: u32, -) -> *mut c_void {panic!("roc_realloc {}", ERR_MSG)} +) -> *mut c_void { + panic!("roc_realloc {}", ERR_MSG) +} #[cfg(windows)] -pub fn roc_dealloc(_ptr: *mut c_void, _alignment: u32) {panic!("roc_dealloc {}", ERR_MSG)} - - +pub fn roc_dealloc(_ptr: *mut c_void, _alignment: u32) { + panic!("roc_dealloc {}", ERR_MSG) +} const REFCOUNT_1: isize = isize::MIN; From b5d8ab7d6fade60e3bf23e586343d0f9738db1f4 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 28 Feb 2022 18:26:55 +0100 Subject: [PATCH 029/132] cleanup --- Cargo.lock | 1 - ast/Cargo.toml | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a3d8e388b..49b421f38d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3195,7 +3195,6 @@ dependencies = [ "roc_target", "roc_types", "roc_unify", - "slab", "snafu", "ven_graph", "winapi", diff --git a/ast/Cargo.toml b/ast/Cargo.toml index b27cdbbf62..86d203cee4 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -24,13 +24,11 @@ bumpalo = { version = "3.8.0", features = ["collections"] } page_size = "0.4.2" snafu = { version = "0.6.10", features = ["backtraces"] } ven_graph = { path = "../vendor/pathfinding" } -slab = "0.4.5" libc = "0.2.106" [dev-dependencies] indoc = "1.0.3" [target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.9", features = ["memoryapi", "heapapi", "synchapi", "winbase", "sysinfoapi"]} -# TODO I'm not sure if all these features are necessary +winapi = { version = "0.3.9", features = ["memoryapi"]} From 4d570b635f72b2732702d308206189a4e0cd31e2 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 28 Feb 2022 19:47:57 +0100 Subject: [PATCH 030/132] made editor WIP more obvious --- BUILDING_FROM_SOURCE.md | 2 +- editor/README.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/BUILDING_FROM_SOURCE.md b/BUILDING_FROM_SOURCE.md index d66b8e0648..e8ea71df1c 100644 --- a/BUILDING_FROM_SOURCE.md +++ b/BUILDING_FROM_SOURCE.md @@ -37,7 +37,7 @@ If you plan on using `nix-shell` regularly, check out [direnv](https://direnv.ne ### Editor -The editor is a WIP and not ready yet to replace your favorite editor, although if you want to try it out on nix, read on. +The editor is a :construction:WIP:construction: and not ready yet to replace your favorite editor, although if you want to try it out on nix, read on. `cargo run edit` should work from NixOS, if you use a nix-shell from inside another OS, follow the instructions below. #### Nvidia GPU diff --git a/editor/README.md b/editor/README.md index 9032e4acb3..e462661928 100644 --- a/editor/README.md +++ b/editor/README.md @@ -1,4 +1,7 @@ + +## :construction: Work In Progress :construction: + The editor is a work in progress, only a limited subset of Roc expressions are currently supported. Unlike most editors, we use projectional or structural editing to edit the [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) directly. This will allow for cool features like excellent auto-complete, refactoring and never needing to format your code. @@ -68,6 +71,7 @@ Important folders/files outside the editor folder: - ast/src/lang/core/ast.rs - ast/src/lang/env.rs + ## Contributing We welcome new contributors :heart: and are happy to help you get started. From 223a9edfd20f42a345b25c10bcb5e8a61b7f2062 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Mon, 28 Feb 2022 19:29:09 +0000 Subject: [PATCH 031/132] Add a REPL page to the website at /repl/index.html --- repl_www/build.sh | 5 +++-- www/build.sh | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/repl_www/build.sh b/repl_www/build.sh index aad87b3fc0..8204560195 100755 --- a/repl_www/build.sh +++ b/repl_www/build.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -set -eux +set -euxo pipefail if [[ ! -d repl_www ]] then @@ -14,7 +14,8 @@ then cargo install wasm-pack fi -WWW_DIR="repl_www/build" +# output directory is first argument or default +WWW_DIR="${1:-repl_www/build}" mkdir -p $WWW_DIR cp repl_www/public/* $WWW_DIR diff --git a/www/build.sh b/www/build.sh index af62488f96..7a1f8008bd 100755 --- a/www/build.sh +++ b/www/build.sh @@ -11,6 +11,12 @@ cd $SCRIPT_RELATIVE_DIR rm -rf build/ cp -r public/ build/ +# Build the repl page +mkdir -p build/repl +pushd .. +repl_www/build.sh www/build/repl +popd + # grab the source code and copy it to Netlify's server; if it's not there, fail the build. pushd build wget https://github.com/rtfeldman/elm-css/files/8037422/roc-source-code.zip From c18befeccfdad7f8b1294f84396a898adad1c34b Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 28 Feb 2022 23:37:33 +0100 Subject: [PATCH 032/132] short-circuit aliases --- compiler/load/src/file.rs | 4 ++-- compiler/solve/src/module.rs | 3 +-- compiler/solve/src/solve.rs | 39 +++++++++++++++++++----------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 0cfa433b12..19b93f2da8 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3102,7 +3102,7 @@ fn run_solve<'a>( } let (solved_subs, solved_env, problems) = - roc_solve::module::run_solve(aliases, rigid_variables, constraint, var_store); + roc_solve::module::run_solve(rigid_variables, constraint, var_store); let mut exposed_vars_by_symbol: MutMap = solved_env.vars_by_symbol.clone(); exposed_vars_by_symbol.retain(|k, _| exposed_symbols.contains(k)); @@ -3115,7 +3115,7 @@ fn run_solve<'a>( exposed_symbols: exposed_symbols.into_iter().collect::>(), solved_types, problems, - aliases: solved_env.aliases, + aliases, }; // Record the final timings diff --git a/compiler/solve/src/module.rs b/compiler/solve/src/module.rs index 72c69112dc..4cf368a460 100644 --- a/compiler/solve/src/module.rs +++ b/compiler/solve/src/module.rs @@ -17,14 +17,13 @@ pub struct SolvedModule { } pub fn run_solve( - aliases: MutMap, rigid_variables: MutMap, constraint: Constraint, var_store: VarStore, ) -> (Solved, solve::Env, Vec) { let env = solve::Env { vars_by_symbol: MutMap::default(), - aliases, + aliases: MutMap::default(), }; let mut subs = Subs::new_from_varstore(var_store); diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 44848a0e35..7bdd0dd9ee 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -82,6 +82,23 @@ pub struct Env { pub aliases: MutMap, } +impl Env { + fn get_var_by_symbol(&self, symbol: &Symbol) -> Option { + self.vars_by_symbol.get(symbol).copied() + } + + fn insert_symbol_var_if_vacant(&mut self, symbol: Symbol, var: Variable) { + match self.vars_by_symbol.entry(symbol) { + Entry::Occupied(_) => { + // keep the existing value + } + Entry::Vacant(vacant) => { + vacant.insert(var); + } + } + } +} + const DEFAULT_POOLS: usize = 8; #[derive(Clone, Debug)] @@ -312,7 +329,7 @@ fn solve( } } Lookup(symbol, expectation, region) => { - match env.vars_by_symbol.get(symbol) { + match env.get_var_by_symbol(symbol) { Some(var) => { // Deep copy the vars associated with this symbol before unifying them. // Otherwise, suppose we have this: @@ -335,7 +352,7 @@ fn solve( // then we copy from that module's Subs into our own. If the value // is being looked up in this module, then we use our Subs as both // the source and destination. - let actual = deep_copy_var(subs, rank, pools, *var); + let actual = deep_copy_var(subs, rank, pools, var); let expected = type_to_var( subs, rank, @@ -484,14 +501,7 @@ fn solve( let mut new_env = env.clone(); for (symbol, loc_var) in local_def_vars.iter() { - match new_env.vars_by_symbol.entry(*symbol) { - Entry::Occupied(_) => { - // keep the existing value - } - Entry::Vacant(vacant) => { - vacant.insert(loc_var.value); - } - } + new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); } stack.push(Work::Constraint { @@ -625,14 +635,7 @@ fn solve( let mut new_env = env.clone(); for (symbol, loc_var) in local_def_vars.iter() { - match new_env.vars_by_symbol.entry(*symbol) { - Entry::Occupied(_) => { - // keep the existing value - } - Entry::Vacant(vacant) => { - vacant.insert(loc_var.value); - } - } + new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); } // Note that this vars_by_symbol is the one returned by the From 751ae125a5ffd504074bc37735c8a4b3a0102916 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 28 Feb 2022 23:41:07 +0100 Subject: [PATCH 033/132] remove aliases from solve Env --- compiler/load/src/file.rs | 3 +-- compiler/solve/src/module.rs | 30 ------------------------------ compiler/solve/src/solve.rs | 3 +-- 3 files changed, 2 insertions(+), 34 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 19b93f2da8..c98eeb615e 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3107,8 +3107,7 @@ fn run_solve<'a>( let mut exposed_vars_by_symbol: MutMap = solved_env.vars_by_symbol.clone(); exposed_vars_by_symbol.retain(|k, _| exposed_symbols.contains(k)); - let solved_types = - roc_solve::module::make_solved_types(&solved_env, &solved_subs, &exposed_vars_by_symbol); + let solved_types = roc_solve::module::make_solved_types(&solved_subs, &exposed_vars_by_symbol); let solved_module = SolvedModule { exposed_vars_by_symbol, diff --git a/compiler/solve/src/module.rs b/compiler/solve/src/module.rs index 4cf368a460..1d88e83b06 100644 --- a/compiler/solve/src/module.rs +++ b/compiler/solve/src/module.rs @@ -23,7 +23,6 @@ pub fn run_solve( ) -> (Solved, solve::Env, Vec) { let env = solve::Env { vars_by_symbol: MutMap::default(), - aliases: MutMap::default(), }; let mut subs = Subs::new_from_varstore(var_store); @@ -43,40 +42,11 @@ pub fn run_solve( } pub fn make_solved_types( - solved_env: &solve::Env, solved_subs: &Solved, exposed_vars_by_symbol: &MutMap, ) -> MutMap { let mut solved_types = MutMap::default(); - for (symbol, alias) in solved_env.aliases.iter() { - let mut args = Vec::with_capacity(alias.type_variables.len()); - - for loc_named_var in alias.type_variables.iter() { - let (name, var) = &loc_named_var.value; - - args.push((name.clone(), SolvedType::new(solved_subs, *var))); - } - - let mut lambda_set_variables = Vec::with_capacity(alias.lambda_set_variables.len()); - for set in alias.lambda_set_variables.iter() { - lambda_set_variables.push(roc_types::solved_types::SolvedLambdaSet( - SolvedType::from_type(solved_subs, &set.0), - )); - } - - let solved_type = SolvedType::from_type(solved_subs, &alias.typ); - let solved_alias = SolvedType::Alias( - *symbol, - args, - lambda_set_variables, - Box::new(solved_type), - alias.kind, - ); - - solved_types.insert(*symbol, solved_alias); - } - // exposed_vars_by_symbol contains the Variables for all the Symbols // this module exposes. We want to convert those into flat SolvedType // annotations which are decoupled from our Subs, because that's how diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 7bdd0dd9ee..79e0b7eb50 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -12,7 +12,7 @@ use roc_types::subs::{ }; use roc_types::types::Type::{self, *}; use roc_types::types::{ - gather_fields_unsorted_iter, Alias, AliasKind, Category, ErrorType, PatternCategory, + gather_fields_unsorted_iter, AliasKind, Category, ErrorType, PatternCategory, }; use roc_unify::unify::{unify, Mode, Unified::*}; use std::collections::hash_map::Entry; @@ -79,7 +79,6 @@ pub enum TypeError { #[derive(Clone, Debug, Default)] pub struct Env { pub vars_by_symbol: MutMap, - pub aliases: MutMap, } impl Env { From 9d82f795b7733ee535619badcd0d240a2a9af66d Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 28 Feb 2022 23:43:58 +0100 Subject: [PATCH 034/132] make it abstract --- compiler/load/src/file.rs | 2 +- compiler/solve/src/module.rs | 4 +--- compiler/solve/src/solve.rs | 6 +++++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index c98eeb615e..a015996034 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3104,7 +3104,7 @@ fn run_solve<'a>( let (solved_subs, solved_env, problems) = roc_solve::module::run_solve(rigid_variables, constraint, var_store); - let mut exposed_vars_by_symbol: MutMap = solved_env.vars_by_symbol.clone(); + let mut exposed_vars_by_symbol: MutMap = solved_env.vars_by_symbol().clone(); exposed_vars_by_symbol.retain(|k, _| exposed_symbols.contains(k)); let solved_types = roc_solve::module::make_solved_types(&solved_subs, &exposed_vars_by_symbol); diff --git a/compiler/solve/src/module.rs b/compiler/solve/src/module.rs index 1d88e83b06..970e38a281 100644 --- a/compiler/solve/src/module.rs +++ b/compiler/solve/src/module.rs @@ -21,9 +21,7 @@ pub fn run_solve( constraint: Constraint, var_store: VarStore, ) -> (Solved, solve::Env, Vec) { - let env = solve::Env { - vars_by_symbol: MutMap::default(), - }; + let env = solve::Env::default(); let mut subs = Subs::new_from_varstore(var_store); diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 79e0b7eb50..efb7e836a6 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -78,10 +78,14 @@ pub enum TypeError { #[derive(Clone, Debug, Default)] pub struct Env { - pub vars_by_symbol: MutMap, + vars_by_symbol: MutMap, } impl Env { + pub fn vars_by_symbol(&self) -> MutMap { + self.vars_by_symbol.clone() + } + fn get_var_by_symbol(&self, symbol: &Symbol) -> Option { self.vars_by_symbol.get(symbol).copied() } From 1ef9546391ff3137c05d0408b45f75c34133daec Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 28 Feb 2022 23:54:35 +0100 Subject: [PATCH 035/132] use Vec instead of MutMap --- compiler/solve/src/solve.rs | 20 ++++++++++++-------- reporting/tests/helpers/mod.rs | 5 +---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index efb7e836a6..a13aa88f96 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -78,25 +78,29 @@ pub enum TypeError { #[derive(Clone, Debug, Default)] pub struct Env { - vars_by_symbol: MutMap, + vars_by_symbol: Vec<(Symbol, Variable)>, } impl Env { pub fn vars_by_symbol(&self) -> MutMap { - self.vars_by_symbol.clone() + self.vars_by_symbol.iter().copied().collect() } fn get_var_by_symbol(&self, symbol: &Symbol) -> Option { - self.vars_by_symbol.get(symbol).copied() + self.vars_by_symbol + .iter() + .find(|(s, _)| s == symbol) + .map(|(_, var)| *var) } fn insert_symbol_var_if_vacant(&mut self, symbol: Symbol, var: Variable) { - match self.vars_by_symbol.entry(symbol) { - Entry::Occupied(_) => { - // keep the existing value + match self.get_var_by_symbol(&symbol) { + None => { + // symbol is not in vars_by_symbol yet; insert it + self.vars_by_symbol.push((symbol, var)) } - Entry::Vacant(vacant) => { - vacant.insert(var); + Some(_) => { + // do nothing } } } diff --git a/reporting/tests/helpers/mod.rs b/reporting/tests/helpers/mod.rs index 544c440164..0485f9da9d 100644 --- a/reporting/tests/helpers/mod.rs +++ b/reporting/tests/helpers/mod.rs @@ -31,10 +31,7 @@ pub fn infer_expr( constraint: &Constraint, expr_var: Variable, ) -> (Content, Subs) { - let env = solve::Env { - aliases: MutMap::default(), - vars_by_symbol: MutMap::default(), - }; + let env = solve::Env::default(); let (solved, _) = solve::run(&env, problems, subs, constraint); let content = solved From d3bbf6d504f77ae60f96e141f766763b8881b8d3 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 1 Mar 2022 00:00:50 +0100 Subject: [PATCH 036/132] struct of arrays! --- compiler/solve/src/solve.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index a13aa88f96..1c3ba0588c 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -78,26 +78,31 @@ pub enum TypeError { #[derive(Clone, Debug, Default)] pub struct Env { - vars_by_symbol: Vec<(Symbol, Variable)>, + symbols: Vec, + variables: Vec, } impl Env { pub fn vars_by_symbol(&self) -> MutMap { - self.vars_by_symbol.iter().copied().collect() + let it1 = self.symbols.iter().copied(); + let it2 = self.variables.iter().copied(); + + it1.zip(it2).collect() } fn get_var_by_symbol(&self, symbol: &Symbol) -> Option { - self.vars_by_symbol + self.symbols .iter() - .find(|(s, _)| s == symbol) - .map(|(_, var)| *var) + .position(|s| s == symbol) + .map(|index| self.variables[index]) } fn insert_symbol_var_if_vacant(&mut self, symbol: Symbol, var: Variable) { - match self.get_var_by_symbol(&symbol) { + match self.symbols.iter().position(|s| *s == symbol) { None => { // symbol is not in vars_by_symbol yet; insert it - self.vars_by_symbol.push((symbol, var)) + self.symbols.push(symbol); + self.variables.push(var); } Some(_) => { // do nothing From 8b457a56c5f9ae7f19a1dc030185fb659fb1449b Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 1 Mar 2022 00:08:56 +0100 Subject: [PATCH 037/132] reduce cloning of Env --- compiler/load/src/file.rs | 2 +- compiler/solve/src/solve.rs | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index a015996034..695be96714 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -3104,7 +3104,7 @@ fn run_solve<'a>( let (solved_subs, solved_env, problems) = roc_solve::module::run_solve(rigid_variables, constraint, var_store); - let mut exposed_vars_by_symbol: MutMap = solved_env.vars_by_symbol().clone(); + let mut exposed_vars_by_symbol: MutMap = solved_env.vars_by_symbol(); exposed_vars_by_symbol.retain(|k, _| exposed_symbols.contains(k)); let solved_types = roc_solve::module::make_solved_types(&solved_subs, &exposed_vars_by_symbol); diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 1c3ba0588c..fc2e73191c 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1,3 +1,4 @@ +use bumpalo::Bump; use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::PresenceConstraint; use roc_can::expected::{Expected, PExpected}; @@ -192,7 +193,11 @@ pub fn run_in_place( mark: Mark::NONE.next(), }; let rank = Rank::toplevel(); + + let arena = Bump::new(); + let state = solve( + &arena, env, state, rank, @@ -212,7 +217,7 @@ enum After { enum Work<'a> { Constraint { - env: Env, + env: &'a Env, rank: Rank, constraint: &'a Constraint, after: Option, @@ -223,6 +228,7 @@ enum Work<'a> { #[allow(clippy::too_many_arguments)] fn solve( + arena: &Bump, env: &Env, mut state: State, rank: Rank, @@ -233,7 +239,7 @@ fn solve( constraint: &Constraint, ) -> State { let mut stack = vec![Work::Constraint { - env: env.clone(), + env, rank, constraint, after: None, @@ -269,7 +275,7 @@ fn solve( // NOTE deviation: elm only copies the env into the state on SaveTheEnvironment let mut copy = state; - copy.env = env; + copy.env = env.clone(); copy } @@ -412,7 +418,7 @@ fn solve( And(sub_constraints) => { for sub_constraint in sub_constraints.iter().rev() { stack.push(Work::Constraint { - env: env.clone(), + env, rank, constraint: sub_constraint, after: None, @@ -484,7 +490,8 @@ fn solve( ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { // TODO: make into `WorkItem` with `After` let state = solve( - &env, + arena, + env, state, rank, pools, @@ -517,7 +524,7 @@ fn solve( } stack.push(Work::Constraint { - env: new_env, + env: arena.alloc(new_env), rank, constraint: ret_con, after: Some(After::CheckForInfiniteTypes(local_def_vars)), @@ -581,7 +588,8 @@ fn solve( env: saved_env, mark, } = solve( - &env, + arena, + env, state, next_rank, pools, @@ -660,7 +668,7 @@ fn solve( // Now solve the body, using the new vars_by_symbol which includes // the assignments' name-to-variable mappings. stack.push(Work::Constraint { - env: new_env, + env: arena.alloc(new_env), rank, constraint: ret_con, after: Some(After::CheckForInfiniteTypes(local_def_vars)), From 32058a3336ce273e3fdbb06841ddb90fdb840eb7 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 28 Feb 2022 19:16:37 -0500 Subject: [PATCH 038/132] Format index.html --- www/public/index.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/www/public/index.html b/www/public/index.html index 8b6d782d41..be1cec60df 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -10,11 +10,12 @@ + Roc REPL diff --git a/repl_www/public/repl.css b/repl_www/public/repl.css new file mode 100644 index 0000000000..ef66d0eced --- /dev/null +++ b/repl_www/public/repl.css @@ -0,0 +1,68 @@ +body { + background-color: #222; + color: #ccc; + font-family: sans-serif; + font-size: 18px; +} +.body-wrapper { + display: flex; + flex-direction: column; + max-width: 900px; + height: 100%; + margin: 0 auto; + padding: 0 24px; +} +h1 { + margin: 32px auto; + color: #eee; + text-align: center; +} +li { + margin: 8px; +} +section.history { + flex: 1; +} +.scroll-wrap { + position: relative; + height: 100%; +} +.scroll { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow: auto; +} +#history-text { + margin: 16px 0; + padding: 8px; +} +#history-text .input { + margin-bottom: 8px; +} +#history-text .output { + margin-bottom: 16px; +} +#history-text .output-ok { + color: #0f8; +} +#history-text .output-error { + color: #f00; +} +.code { + font-family: "Courier New", Courier, monospace; + background-color: #111; + color: #fff; +} +section.source { + display: flex; + flex-direction: column; +} + +section.source input { + height: 32px; + padding: 8px; + margin-bottom: 16px; +} From 1ce018221a5ada809e0e9764f931e96ddfd17d7a Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Wed, 2 Mar 2022 14:55:33 -0800 Subject: [PATCH 082/132] run niv update to get rust 1.58.0 in nix --- nix/sources.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index a7c8c261c0..04dc2dbb42 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -5,10 +5,10 @@ "homepage": "https://github.com/nmattia/niv", "owner": "nmattia", "repo": "niv", - "rev": "5830a4dd348d77e39a0f3c4c762ff2663b602d4c", - "sha256": "1d3lsrqvci4qz2hwjrcnd8h5vfkg8aypq3sjd4g3izbc8frwz5sm", + "rev": "9cb7ef336bb71fd1ca84fc7f2dff15ef4b033f2a", + "sha256": "1ajyqr8zka1zlb25jx1v4xys3zqmdy3prbm1vxlid6ah27a8qnzh", "type": "tarball", - "url": "https://github.com/nmattia/niv/archive/5830a4dd348d77e39a0f3c4c762ff2663b602d4c.tar.gz", + "url": "https://github.com/nmattia/niv/archive/9cb7ef336bb71fd1ca84fc7f2dff15ef4b033f2a.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs": { @@ -17,10 +17,10 @@ "homepage": "", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fe6f208d68ac254873b659db9676d44dea9b0555", - "sha256": "0ybvy1zx97k811bz73xmgsb41d33i2kr2dfqcxzq9m9h958178nq", + "rev": "ed02c2ba0384b2800db41333045a6fb781f12aac", + "sha256": "040rawxqbpblxpsq73qxlk25my2cm0g3gx1pksiacsj15q5fi84q", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/fe6f208d68ac254873b659db9676d44dea9b0555.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/ed02c2ba0384b2800db41333045a6fb781f12aac.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs-unstable": { @@ -29,10 +29,10 @@ "homepage": "", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ea171bc81fcb3c6f21deeb46dbc10000087777ef", - "sha256": "15hh28c98kb6pf7wgydc07bx2ivq04a2cay5mhwnqk5cpa8dbiap", + "rev": "684c73c9e6ac8f4d0c6dea3251292e758ac375b5", + "sha256": "0hl2nzizn4pwd3sn9gxkngzn88k9in01xm14afpj7716j8y0j2qa", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/ea171bc81fcb3c6f21deeb46dbc10000087777ef.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/684c73c9e6ac8f4d0c6dea3251292e758ac375b5.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } From 7b489e81b9b3a0fb7b93f8c2437de0970fb3c54d Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Mar 2022 18:34:47 -0500 Subject: [PATCH 083/132] Have web repl use /repl as its URL root --- repl_www/README.md | 2 +- repl_www/build.sh | 2 +- repl_www/public/index.html | 48 +++++++++++++++++++------------------- www/build.sh | 4 +++- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/repl_www/README.md b/repl_www/README.md index c7f7242c7e..b5fa57b571 100644 --- a/repl_www/README.md +++ b/repl_www/README.md @@ -25,7 +25,7 @@ python3 -m http.server ``` ### 3. Open your browser -You should be able to find the Roc REPL at http://127.0.0.1:8000 (or wherever your web server said when it started up.) +You should be able to find the Roc REPL at http://127.0.0.1:8000/repl (or whatever port your web server mentioned when it started up.) **Warning:** This is work in progress! Not all language features are implemented yet, error messages don't look nice yet, up/down arrows don't work for history, etc. diff --git a/repl_www/build.sh b/repl_www/build.sh index 8204560195..fcaa01ee5f 100755 --- a/repl_www/build.sh +++ b/repl_www/build.sh @@ -15,7 +15,7 @@ then fi # output directory is first argument or default -WWW_DIR="${1:-repl_www/build}" +WWW_DIR="${1:-repl_www/build/repl}" mkdir -p $WWW_DIR cp repl_www/public/* $WWW_DIR diff --git a/repl_www/public/index.html b/repl_www/public/index.html index 6f300c955c..96a2740c4a 100644 --- a/repl_www/public/index.html +++ b/repl_www/public/index.html @@ -1,28 +1,28 @@ - - - Roc REPL - - -
-
-

The rockin' Roc REPL

-
-
-
-
-
-
+ + Roc REPL + + + + + +
+
+

The rockin' Roc REPL

+
+ +
+
+
+
+
+ +
+ +
+
+ + -
- -
-
- - diff --git a/www/build.sh b/www/build.sh index d993936218..5f6703d798 100755 --- a/www/build.sh +++ b/www/build.sh @@ -17,13 +17,15 @@ 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. mkdir -p repl -cd repl +pushd repl 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 cp ../../../repl_www/public/* . popd +popd + # pushd .. # echo 'Generating docs...' # cargo --version From 87f5c04c6c9dfbeeec6b7c47a1ef06a8d9aaffcb Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Mar 2022 18:42:23 -0500 Subject: [PATCH 084/132] Add autofocus to web repl --- repl_www/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repl_www/public/index.html b/repl_www/public/index.html index 96a2740c4a..38631daf91 100644 --- a/repl_www/public/index.html +++ b/repl_www/public/index.html @@ -19,7 +19,7 @@
- +
From cc105b3ac21fdf7e94fe771cf62c2747e33955fd Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Mar 2022 18:43:59 -0500 Subject: [PATCH 085/132] Make a change to index.html so Netlify redeploys --- www/public/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/www/public/index.html b/www/public/index.html index d4dbf69de1..be1cec60df 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -50,4 +50,3 @@ - From 25f0948cf62beeeaa9908a4dbab8adf5bdb0e09e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Mar 2022 18:45:55 -0500 Subject: [PATCH 086/132] Tell Netlify to always build on push --- www/netlify.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/www/netlify.toml b/www/netlify.toml index 4d8adfad0c..1ec7657938 100644 --- a/www/netlify.toml +++ b/www/netlify.toml @@ -6,6 +6,8 @@ [build] publish = "build/" command = "bash netlify.sh" + # Always build on push - see https://answers.netlify.com/t/builds-cancelled-for-a-new-branch-due-to-no-content-change/17169/2 + ignore = "/bin/false" [[headers]] for = "/*" From 5b4615764ca59fcc34af9f8962eae30a784f6f17 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Mar 2022 20:07:35 -0500 Subject: [PATCH 087/132] Don't use in repl_www It seems to work inconsistently across browsers. --- repl_www/build.sh | 9 ++++++++- repl_www/public/index.html | 5 ++--- repl_www/public/repl.js | 4 ++-- www/build.sh | 6 ++++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/repl_www/build.sh b/repl_www/build.sh index fcaa01ee5f..d787340722 100755 --- a/repl_www/build.sh +++ b/repl_www/build.sh @@ -17,7 +17,14 @@ fi # output directory is first argument or default WWW_DIR="${1:-repl_www/build/repl}" mkdir -p $WWW_DIR -cp repl_www/public/* $WWW_DIR + +# End up with `repl/index.html` and everything else at the root directory. +# We want all the assets to be at the root because the files auto-generated by `cargo` expect them to be there. +pushd repl_www/public +mv index.html index_repl.html +cp -r . $WWW_DIR +mv $WWW_DIR/index-repl.html ./index.html +popd # When debugging the REPL, use `REPL_DEBUG=1 repl_www/build.sh` if [ -n "${REPL_DEBUG:-}" ] diff --git a/repl_www/public/index.html b/repl_www/public/index.html index 38631daf91..b2a3db7128 100644 --- a/repl_www/public/index.html +++ b/repl_www/public/index.html @@ -2,8 +2,7 @@ Roc REPL - - + @@ -22,7 +21,7 @@ - + diff --git a/repl_www/public/repl.js b/repl_www/public/repl.js index 8c22bdc9f8..008a08c8e7 100644 --- a/repl_www/public/repl.js +++ b/repl_www/public/repl.js @@ -2,8 +2,8 @@ window.js_create_app = js_create_app; window.js_run_app = js_run_app; window.js_get_result_and_memory = js_get_result_and_memory; -import * as roc_repl_wasm from "./roc_repl_wasm.js"; -import { getMockWasiImports } from "./wasi.js"; +import * as roc_repl_wasm from "/roc_repl_wasm.js"; +import { getMockWasiImports } from "/wasi.js"; // ---------------------------------------------------------------------------- // REPL state diff --git a/www/build.sh b/www/build.sh index 5f6703d798..78bff6d8e6 100755 --- a/www/build.sh +++ b/www/build.sh @@ -22,6 +22,12 @@ wget https://github.com/brian-carroll/mock-repl/files/8167902/roc_repl_wasm.tar. tar xzvf roc_repl_wasm.tar.gz rm roc_repl_wasm.tar.gz cp ../../../repl_www/public/* . + +# End up with `repl/index.html` and everything else at the root directory. +# We want all the assets to be at the root because the files auto-generated by `cargo` expect them to be there. +mv index.html index_repl.html +mv ./* .. +mv ../index_repl.html ./index.html popd popd From 3938dd476db5deffcb7a552c3793c64015c17f03 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 3 Mar 2022 00:22:54 -0500 Subject: [PATCH 088/132] Capitalize DOCTYPE html in index.html --- www/public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/public/index.html b/www/public/index.html index be1cec60df..b8b5835b99 100644 --- a/www/public/index.html +++ b/www/public/index.html @@ -1,4 +1,4 @@ - + From 70b739bbfc935a0b8edd0381bbe8abb2f7db9ec8 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Wed, 2 Mar 2022 23:58:06 -0500 Subject: [PATCH 089/132] Update CSP to allow wasm compilation in web repl --- www/netlify.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/www/netlify.toml b/www/netlify.toml index 1ec7657938..640b5268f9 100644 --- a/www/netlify.toml +++ b/www/netlify.toml @@ -14,7 +14,10 @@ [headers.values] X-Frame-Options = "DENY" X-XSS-Protection = "1; mode=block" - Content-Security-Policy = "default-src 'self'; img-src *;" + # unsafe-eval is needed for wasm compilation in the repl to work on Safari and Chrome; + # otherwise they block it. + # TODO figure out how to tell Netlify to apply that policy only to the repl, not to everything. + Content-Security-Policy = "default-src 'self'; img-src *; script-src 'self' 'unsafe-eval';" X-Content-Type-Options = "nosniff" # Redirect roc-lang.org/authors to the AUTHORS file in this repo From 37d175b0866f37fe576b6d78178b62cf601b23b7 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 3 Mar 2022 00:23:47 -0500 Subject: [PATCH 090/132] Add DOCTYPE html tag to roc repl page --- repl_www/public/index.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/repl_www/public/index.html b/repl_www/public/index.html index b2a3db7128..2f0dd58caf 100644 --- a/repl_www/public/index.html +++ b/repl_www/public/index.html @@ -1,3 +1,5 @@ + + From 54a070893e931da07ef078de07970b9103bfabea Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Thu, 3 Mar 2022 00:26:33 -0500 Subject: [PATCH 091/132] Revert "Add DOCTYPE html tag to roc repl page" This breaks the page layout, somehow This reverts commit 37d175b0866f37fe576b6d78178b62cf601b23b7. --- repl_www/public/index.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/repl_www/public/index.html b/repl_www/public/index.html index 2f0dd58caf..b2a3db7128 100644 --- a/repl_www/public/index.html +++ b/repl_www/public/index.html @@ -1,5 +1,3 @@ - - From da89152fef7b0e7a9738f664b13d7c2da80a7597 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 3 Mar 2022 08:32:32 +0100 Subject: [PATCH 092/132] fix static assert --- compiler/can/src/constraint.rs | 3 +-- compiler/load/src/file.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/can/src/constraint.rs b/compiler/can/src/constraint.rs index b98184536f..e134002c50 100644 --- a/compiler/can/src/constraint.rs +++ b/compiler/can/src/constraint.rs @@ -435,8 +435,7 @@ impl Constraints { } } -static_assertions::assert_eq_size!([u8; 4 * 8], Constraint); -static_assertions::assert_eq_size!([u8; 3 * 8 + 4], LetConstraint); +static_assertions::assert_eq_size!([u8; 3 * 8], Constraint); #[derive(Debug, Clone, PartialEq)] pub enum Constraint { diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 918fe56330..b7cfd80be4 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -1039,7 +1039,7 @@ fn load<'a>( ) -> Result, LoadingProblem<'a>> { // When compiling to wasm, we cannot spawn extra threads // so we have a single-threaded implementation - if cfg!(target_family = "wasm") { + if true || cfg!(target_family = "wasm") { load_single_threaded( arena, load_start, From 22f571ef3cdd2f926eacad01537141d6a7863eef Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Thu, 3 Mar 2022 08:30:35 +0000 Subject: [PATCH 093/132] Simplify www build scripts by putting the REPL HTML where it needs to end up --- repl_www/build.sh | 16 ++++++---------- repl_www/public/{ => repl}/index.html | 0 www/build.sh | 11 +---------- 3 files changed, 7 insertions(+), 20 deletions(-) rename repl_www/public/{ => repl}/index.html (100%) diff --git a/repl_www/build.sh b/repl_www/build.sh index d787340722..419369c689 100755 --- a/repl_www/build.sh +++ b/repl_www/build.sh @@ -15,16 +15,12 @@ then fi # output directory is first argument or default -WWW_DIR="${1:-repl_www/build/repl}" -mkdir -p $WWW_DIR +WWW_ROOT="${1:-repl_www/build}" +mkdir -p $WWW_ROOT # End up with `repl/index.html` and everything else at the root directory. # We want all the assets to be at the root because the files auto-generated by `cargo` expect them to be there. -pushd repl_www/public -mv index.html index_repl.html -cp -r . $WWW_DIR -mv $WWW_DIR/index-repl.html ./index.html -popd +cp -r repl_www/public/* $WWW_ROOT # When debugging the REPL, use `REPL_DEBUG=1 repl_www/build.sh` if [ -n "${REPL_DEBUG:-}" ] @@ -36,10 +32,10 @@ else wasm-pack build --target web repl_wasm fi -cp repl_wasm/pkg/*.wasm $WWW_DIR +cp repl_wasm/pkg/*.wasm $WWW_ROOT # Copy the JS from wasm_bindgen, replacing its invalid `import` statement with a `var`. # The JS import from the invalid path 'env', seems to be generated when there are unresolved symbols. BINDGEN_FILE="roc_repl_wasm.js" -echo 'var __wbg_star0 = { now: Date.now };' > $WWW_DIR/$BINDGEN_FILE -grep -v '^import' repl_wasm/pkg/$BINDGEN_FILE >> $WWW_DIR/$BINDGEN_FILE +echo 'var __wbg_star0 = { now: Date.now };' > $WWW_ROOT/$BINDGEN_FILE +grep -v '^import' repl_wasm/pkg/$BINDGEN_FILE >> $WWW_ROOT/$BINDGEN_FILE diff --git a/repl_www/public/index.html b/repl_www/public/repl/index.html similarity index 100% rename from repl_www/public/index.html rename to repl_www/public/repl/index.html diff --git a/www/build.sh b/www/build.sh index 78bff6d8e6..ae07359997 100755 --- a/www/build.sh +++ b/www/build.sh @@ -16,19 +16,10 @@ pushd 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. -mkdir -p repl -pushd repl 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 -cp ../../../repl_www/public/* . - -# End up with `repl/index.html` and everything else at the root directory. -# We want all the assets to be at the root because the files auto-generated by `cargo` expect them to be there. -mv index.html index_repl.html -mv ./* .. -mv ../index_repl.html ./index.html -popd +cp -r ../../repl_www/public/* . popd From 49be6da565c71c5205d8f78ce7c831b0b6936a35 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Thu, 3 Mar 2022 08:44:04 +0000 Subject: [PATCH 094/132] repl_www: fix DOCTYPE and CSS layout Today I learned that you need to explicitly set height: 100% on the and tags, unless you're in quirks mode! And the takes you out of quirks mode. Firefox complains if we don't have the DOCTYPE --- repl_www/public/repl.css | 4 ++++ repl_www/public/repl/index.html | 2 ++ 2 files changed, 6 insertions(+) diff --git a/repl_www/public/repl.css b/repl_www/public/repl.css index ef66d0eced..49da4116ec 100644 --- a/repl_www/public/repl.css +++ b/repl_www/public/repl.css @@ -1,4 +1,8 @@ +html { + height: 100%; +} body { + height: 100%; background-color: #222; color: #ccc; font-family: sans-serif; diff --git a/repl_www/public/repl/index.html b/repl_www/public/repl/index.html index b2a3db7128..2f0dd58caf 100644 --- a/repl_www/public/repl/index.html +++ b/repl_www/public/repl/index.html @@ -1,3 +1,5 @@ + + From 3d30bcef03f2b6b7a8739b8fa8f035cf158e1a45 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 3 Mar 2022 10:13:52 +0100 Subject: [PATCH 095/132] store filenames out of band --- compiler/can/src/constraint.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/can/src/constraint.rs b/compiler/can/src/constraint.rs index e134002c50..489dc8e8ef 100644 --- a/compiler/can/src/constraint.rs +++ b/compiler/can/src/constraint.rs @@ -18,6 +18,7 @@ pub struct Constraints { pub expectations: Vec>, pub pattern_expectations: Vec>, pub includes_tags: Vec, + pub strings: Vec<&'static str>, } impl Default for Constraints { @@ -38,6 +39,7 @@ impl Constraints { let expectations = Vec::new(); let pattern_expectations = Vec::new(); let includes_tags = Vec::new(); + let strings = Vec::new(); types.extend([Type::EmptyRec, Type::EmptyTagUnion]); @@ -83,6 +85,7 @@ impl Constraints { expectations, pattern_expectations, includes_tags, + strings, } } @@ -427,11 +430,10 @@ impl Constraints { filename: &'static str, line_number: u32, ) -> Constraint { - let type_index = Index::new(self.types.len() as _); + let type_index = Index::push_new(&mut self.types, typ); + let string_index = Index::push_new(&mut self.strings, filename); - self.types.push(typ); - - Constraint::Store(type_index, variable, filename, line_number) + Constraint::Store(type_index, variable, string_index, line_number) } } @@ -440,7 +442,7 @@ static_assertions::assert_eq_size!([u8; 3 * 8], Constraint); #[derive(Debug, Clone, PartialEq)] pub enum Constraint { Eq(Index, Index>, Index, Region), - Store(Index, Variable, &'static str, u32), + Store(Index, Variable, Index<&'static str>, u32), Lookup(Symbol, Index>, Region), Pattern( Index, From a53a5b718effa67997174aa3af93ff2c26ce4017 Mon Sep 17 00:00:00 2001 From: Folkert Date: Thu, 3 Mar 2022 10:36:18 +0100 Subject: [PATCH 096/132] clippy --- compiler/load/src/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index b7cfd80be4..918fe56330 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -1039,7 +1039,7 @@ fn load<'a>( ) -> Result, LoadingProblem<'a>> { // When compiling to wasm, we cannot spawn extra threads // so we have a single-threaded implementation - if true || cfg!(target_family = "wasm") { + if cfg!(target_family = "wasm") { load_single_threaded( arena, load_start, From 6feac21b9b9c9942e40e381a78a3abb9ed3417a2 Mon Sep 17 00:00:00 2001 From: Derek Gustafson Date: Thu, 3 Mar 2022 10:24:05 -0500 Subject: [PATCH 097/132] Remove using a string as an intermediate form in Dec.fromF64. --- compiler/builtins/bitcode/src/dec.zig | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/builtins/bitcode/src/dec.zig b/compiler/builtins/bitcode/src/dec.zig index ca24c12641..2523519bfa 100644 --- a/compiler/builtins/bitcode/src/dec.zig +++ b/compiler/builtins/bitcode/src/dec.zig @@ -26,21 +26,20 @@ pub const RocDec = extern struct { return .{ .num = num * one_point_zero_i128 }; } - // TODO: There's got to be a better way to do this other than converting to Str pub fn fromF64(num: f64) ?RocDec { - var digit_bytes: [19]u8 = undefined; // 19 = max f64 digits + '.' + '-' - var fbs = std.io.fixedBufferStream(digit_bytes[0..]); - std.fmt.formatFloatDecimal(num, .{}, fbs.writer()) catch - return null; + var result: f64 = num * comptime @intToFloat(f64, one_point_zero_i128); - var dec = RocDec.fromStr(RocStr.init(&digit_bytes, fbs.pos)); - - if (dec) |d| { - return d; - } else { + if (result > comptime @intToFloat(f64, math.maxInt(i128))) { return null; } + + if (result < comptime @intToFloat(f64, math.minInt(i128))) { + return null; + } + + var ret: RocDec = .{ .num = @floatToInt(i128, result) }; + return ret; } pub fn fromStr(roc_str: RocStr) ?RocDec { @@ -729,6 +728,11 @@ test "fromF64" { try expectEqual(RocDec{ .num = 25500000000000000000 }, dec.?); } +test "fromF64 overflow" { + var dec = RocDec.fromF64(1e308); + try expectEqual(dec, null); +} + test "fromStr: empty" { var roc_str = RocStr.init("", 0); var dec = RocDec.fromStr(roc_str); From b6ad04b0f1a5178ff3b77a0bb13d27fafa9af231 Mon Sep 17 00:00:00 2001 From: Derek Gustafson Date: Thu, 3 Mar 2022 11:14:58 -0500 Subject: [PATCH 098/132] Fix zig fmt error. --- compiler/builtins/bitcode/src/dec.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/builtins/bitcode/src/dec.zig b/compiler/builtins/bitcode/src/dec.zig index 2523519bfa..e8ec71e07a 100644 --- a/compiler/builtins/bitcode/src/dec.zig +++ b/compiler/builtins/bitcode/src/dec.zig @@ -27,7 +27,6 @@ pub const RocDec = extern struct { } pub fn fromF64(num: f64) ?RocDec { - var result: f64 = num * comptime @intToFloat(f64, one_point_zero_i128); if (result > comptime @intToFloat(f64, math.maxInt(i128))) { From b9365d06e3c409c627a40d21e60d441b7d6b5e75 Mon Sep 17 00:00:00 2001 From: Philippe Vinchon Date: Thu, 3 Mar 2022 21:14:50 +0000 Subject: [PATCH 099/132] Allow multi string in RePL --- compiler/fmt/src/lib.rs | 1 - repl_test/src/tests.rs | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/fmt/src/lib.rs b/compiler/fmt/src/lib.rs index 1d99edbcd6..23667d9f4c 100644 --- a/compiler/fmt/src/lib.rs +++ b/compiler/fmt/src/lib.rs @@ -55,7 +55,6 @@ impl<'a> Buf<'a> { pub fn push_str_allow_spaces(&mut self, s: &str) { debug_assert!(!self.beginning_of_line); - debug_assert!(!s.contains('\n')); self.flush_spaces(); diff --git a/repl_test/src/tests.rs b/repl_test/src/tests.rs index d455af342d..b5d949e323 100644 --- a/repl_test/src/tests.rs +++ b/repl_test/src/tests.rs @@ -519,11 +519,11 @@ fn four_element_record() { ); } -// #[test] -// fn multiline_string() { -// // If a string contains newlines, format it as a multiline string in the output -// expect_success(r#""\n\nhi!\n\n""#, "\"\"\"\n\nhi!\n\n\"\"\""); -// } +#[test] +fn multiline_string() { + // If a string contains newlines, format it as a multiline string in the output + expect_success(r#""\n\nhi!\n\n""#, "\"\n\nhi!\n\n\" : Str"); +} #[test] fn list_of_3_field_records() { From 8937356b0e6214c7600c4824b5386f99b516032c Mon Sep 17 00:00:00 2001 From: Philippe Vinchon Date: Thu, 3 Mar 2022 21:36:00 +0000 Subject: [PATCH 100/132] Use textarea instead of input --- repl_www/public/repl.css | 2 +- repl_www/public/repl/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/repl_www/public/repl.css b/repl_www/public/repl.css index 49da4116ec..f39bccbff6 100644 --- a/repl_www/public/repl.css +++ b/repl_www/public/repl.css @@ -65,7 +65,7 @@ section.source { flex-direction: column; } -section.source input { +section.source textarea { height: 32px; padding: 8px; margin-bottom: 16px; diff --git a/repl_www/public/repl/index.html b/repl_www/public/repl/index.html index 2f0dd58caf..5bc4ac7e76 100644 --- a/repl_www/public/repl/index.html +++ b/repl_www/public/repl/index.html @@ -20,7 +20,7 @@
- +
From f2adf71873674a3e6a577279acf16352b5d44525 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Fri, 4 Mar 2022 14:38:40 +0000 Subject: [PATCH 101/132] Move alias analysis to its own crate instead of roc_mono Shrinks roc_repl_wasm by 400kB (~8%) --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + compiler/alias_analysis/Cargo.toml | 13 +++++++++++++ .../src/lib.rs} | 14 +++++++------- compiler/gen_llvm/Cargo.toml | 1 + compiler/gen_llvm/src/llvm/build.rs | 15 ++++++--------- compiler/mono/src/lib.rs | 1 - 7 files changed, 39 insertions(+), 17 deletions(-) create mode 100644 compiler/alias_analysis/Cargo.toml rename compiler/{mono/src/alias_analysis.rs => alias_analysis/src/lib.rs} (99%) diff --git a/Cargo.lock b/Cargo.lock index 1eb1cc622b..52e42b5c27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3263,6 +3263,16 @@ dependencies = [ "libc", ] +[[package]] +name = "roc_alias_analysis" +version = "0.1.0" +dependencies = [ + "morphic_lib", + "roc_collections", + "roc_module", + "roc_mono", +] + [[package]] name = "roc_ast" version = "0.1.0" @@ -3559,6 +3569,7 @@ dependencies = [ "bumpalo", "inkwell 0.1.0", "morphic_lib", + "roc_alias_analysis", "roc_builtins", "roc_collections", "roc_error_macros", diff --git a/Cargo.toml b/Cargo.toml index bf616e0bfe..f02c317fbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "compiler/solve", "compiler/fmt", "compiler/mono", + "compiler/alias_analysis", "compiler/test_mono", "compiler/load", "compiler/gen_llvm", diff --git a/compiler/alias_analysis/Cargo.toml b/compiler/alias_analysis/Cargo.toml new file mode 100644 index 0000000000..34cfb7666a --- /dev/null +++ b/compiler/alias_analysis/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Roc Contributors"] +edition = "2018" +license = "UPL-1.0" +name = "roc_alias_analysis" +version = "0.1.0" + +[dependencies] +morphic_lib = {path = "../../vendor/morphic_lib"} +roc_collections = {path = "../collections"} +roc_module = {path = "../module"} +roc_mono = {path = "../mono"} + diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/alias_analysis/src/lib.rs similarity index 99% rename from compiler/mono/src/alias_analysis.rs rename to compiler/alias_analysis/src/lib.rs index db061cf395..f9d38cc708 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/alias_analysis/src/lib.rs @@ -8,11 +8,11 @@ use roc_collections::all::{MutMap, MutSet}; use roc_module::low_level::LowLevel; use roc_module::symbol::Symbol; -use crate::ir::{ +use roc_mono::ir::{ Call, CallType, Expr, HigherOrderLowLevel, HostExposedLayouts, ListLiteralElement, Literal, ModifyRc, OptLevel, Proc, Stmt, }; -use crate::layout::{Builtin, Layout, RawFunctionLayout, UnionLayout}; +use roc_mono::layout::{Builtin, Layout, RawFunctionLayout, UnionLayout}; // just using one module for now pub const MOD_APP: ModName = ModName(b"UserApp"); @@ -110,7 +110,7 @@ fn bytes_as_ascii(bytes: &[u8]) -> String { pub fn spec_program<'a, I>( opt_level: OptLevel, - entry_point: crate::ir::EntryPoint<'a>, + entry_point: roc_mono::ir::EntryPoint<'a>, procs: I, ) -> Result where @@ -266,7 +266,7 @@ fn terrible_hack(builder: &mut FuncDefBuilder, block: BlockId, type_id: TypeId) } fn build_entry_point( - layout: crate::ir::ProcLayout, + layout: roc_mono::ir::ProcLayout, func_name: FuncName, host_exposed_functions: &[([u8; SIZE], &[Layout])], ) -> Result { @@ -363,7 +363,7 @@ fn proc_spec<'a>(proc: &Proc<'a>) -> Result<(FuncDef, MutSet>)> #[derive(Default)] struct Env<'a> { symbols: MutMap, - join_points: MutMap, + join_points: MutMap, type_names: MutSet>, } @@ -711,7 +711,7 @@ fn call_spec( passed_function, .. }) => { - use crate::low_level::HigherOrder::*; + use roc_mono::low_level::HigherOrder::*; let array = passed_function.specialization_id.to_bytes(); let spec_var = CalleeSpecVar(&array); @@ -1196,7 +1196,7 @@ fn lowlevel_spec( block: BlockId, layout: &Layout, op: &LowLevel, - update_mode: crate::ir::UpdateModeId, + update_mode: roc_mono::ir::UpdateModeId, arguments: &[Symbol], ) -> Result { use LowLevel::*; diff --git a/compiler/gen_llvm/Cargo.toml b/compiler/gen_llvm/Cargo.toml index 96dfaaf58e..26335be9ff 100644 --- a/compiler/gen_llvm/Cargo.toml +++ b/compiler/gen_llvm/Cargo.toml @@ -7,6 +7,7 @@ license = "UPL-1.0" edition = "2018" [dependencies] +roc_alias_analysis = { path = "../alias_analysis" } roc_collections = { path = "../collections" } roc_module = { path = "../module" } roc_builtins = { path = "../builtins" } diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index a80b1becb4..b0cd6b0921 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -710,7 +710,7 @@ fn promote_to_main_function<'a, 'ctx, 'env>( top_level: ProcLayout<'a>, ) -> (&'static str, FunctionValue<'ctx>) { let it = top_level.arguments.iter().copied(); - let bytes = roc_mono::alias_analysis::func_name_bytes_help(symbol, it, &top_level.result); + let bytes = roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result); let func_name = FuncName(&bytes); let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); @@ -4045,7 +4045,7 @@ pub fn build_proc_headers<'a, 'ctx, 'env>( // Populate Procs further and get the low-level Expr from the canonical Expr let mut headers = Vec::with_capacity_in(procedures.len(), env.arena); for ((symbol, layout), proc) in procedures { - let name_bytes = roc_mono::alias_analysis::func_name_bytes(&proc); + let name_bytes = roc_alias_analysis::func_name_bytes(&proc); let func_name = FuncName(&name_bytes); let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); @@ -4110,7 +4110,7 @@ fn build_procedures_help<'a, 'ctx, 'env>( let it = procedures.iter().map(|x| x.1); - let solutions = match roc_mono::alias_analysis::spec_program(opt_level, entry_point, it) { + let solutions = match roc_alias_analysis::spec_program(opt_level, entry_point, it) { Err(e) => panic!("Error in alias analysis: {}", e), Ok(solutions) => solutions, }; @@ -4118,7 +4118,7 @@ fn build_procedures_help<'a, 'ctx, 'env>( let solutions = env.arena.alloc(solutions); let mod_solutions = solutions - .mod_solutions(roc_mono::alias_analysis::MOD_APP) + .mod_solutions(roc_alias_analysis::MOD_APP) .unwrap(); // Add all the Proc headers to the module. @@ -4470,11 +4470,8 @@ pub fn build_proc<'a, 'ctx, 'env>( // * roc__mainForHost_1_Update_result_size() -> i64 let it = top_level.arguments.iter().copied(); - let bytes = roc_mono::alias_analysis::func_name_bytes_help( - symbol, - it, - &top_level.result, - ); + let bytes = + roc_alias_analysis::func_name_bytes_help(symbol, it, &top_level.result); let func_name = FuncName(&bytes); let func_solutions = mod_solutions.func_solutions(func_name).unwrap(); diff --git a/compiler/mono/src/lib.rs b/compiler/mono/src/lib.rs index b48c0e6fd6..d927845749 100644 --- a/compiler/mono/src/lib.rs +++ b/compiler/mono/src/lib.rs @@ -2,7 +2,6 @@ // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant, clippy::upper_case_acronyms)] -pub mod alias_analysis; pub mod borrow; pub mod code_gen_help; pub mod inc_dec; From db06c10b5fa55249e866e40a847d3f68383d1675 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 4 Mar 2022 23:02:10 +0100 Subject: [PATCH 102/132] be smarter --- compiler/constrain/src/expr.rs | 85 +++++++++++++--------- compiler/constrain/src/pattern.rs | 14 ++-- compiler/solve/tests/solve_expr.rs | 38 ++++++++++ compiler/types/src/types.rs | 112 +++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+), 40 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index da99c6c50e..2d3d131019 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -11,7 +11,7 @@ use roc_can::expected::PExpected; use roc_can::expr::Expr::{self, *}; use roc_can::expr::{ClosureData, Field, WhenBranch}; use roc_can::pattern::Pattern; -use roc_collections::all::{ImMap, Index, MutSet, SendMap}; +use roc_collections::all::{ImMap, Index, MutMap, SendMap}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::{Loc, Region}; @@ -52,7 +52,7 @@ pub struct Env { /// Whenever we encounter a user-defined type variable (a "rigid" var for short), /// for example `a` in the annotation `identity : a -> a`, we add it to this /// map so that expressions within that annotation can share these vars. - pub rigids: ImMap, + pub rigids: MutMap, pub home: ModuleId, } @@ -693,7 +693,7 @@ pub fn constrain_expr( let constraint = constrain_expr( &Env { home: env.home, - rigids: ImMap::default(), + rigids: MutMap::default(), }, region, &loc_expr.value, @@ -1170,7 +1170,7 @@ pub fn constrain_decls(home: ModuleId, decls: &[Declaration]) -> Constraint { let mut env = Env { home, - rigids: ImMap::default(), + rigids: MutMap::default(), }; for decl in decls.iter().rev() { @@ -1227,13 +1227,29 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint { def_pattern_state.vars.push(expr_var); let mut new_rigids = Vec::new(); - let expr_con = match &def.annotation { Some(annotation) => { let arity = annotation.signature.arity(); let rigids = &env.rigids; let mut ftv = rigids.clone(); + /* + let mut new_rigids = Vec::new(); + + // pub wildcards: Vec, + // pub var_by_name: SendMap, + 'outer: for (outer_name, outer_var) in rigids.iter() { + for (inner_name, inner_var) in annotation.introduced_variables.var_by_name.iter() { + if outer_name == inner_name { + debug_assert_eq!(inner_var, outer_var); + continue 'outer; + } + } + + // the inner name is not in the outer scope; it's introduced here + } + */ + let signature = instantiate_rigids( &annotation.signature, &annotation.introduced_variables, @@ -1514,46 +1530,47 @@ fn instantiate_rigids( annotation: &Type, introduced_vars: &IntroducedVariables, new_rigids: &mut Vec, - ftv: &mut ImMap, // rigids defined before the current annotation + ftv: &mut MutMap, // rigids defined before the current annotation loc_pattern: &Loc, headers: &mut SendMap>, ) -> Type { - let mut annotation = annotation.clone(); + // find out if rigid type variables first occur in this annotation, + // or if they are already introduced in an outer annotation let mut rigid_substitution: ImMap = ImMap::default(); - - let outside_rigids: MutSet = ftv.values().copied().collect(); - for (name, var) in introduced_vars.var_by_name.iter() { - if let Some(existing_rigid) = ftv.get(name) { - rigid_substitution.insert(*var, Type::Variable(*existing_rigid)); - } else { - // It's possible to use this rigid in nested defs - ftv.insert(name.clone(), *var); + use std::collections::hash_map::Entry::*; + + match ftv.entry(name.clone()) { + Occupied(occupied) => { + let existing_rigid = occupied.get(); + rigid_substitution.insert(*var, Type::Variable(*existing_rigid)); + } + Vacant(vacant) => { + // It's possible to use this rigid in nested defs + vacant.insert(*var); + new_rigids.push(*var); + } } } - // Instantiate rigid variables + // wildcards are always freshly introduced in this annotation + for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { + ftv.insert(format!("*{}", i).into(), *wildcard); + new_rigids.push(*wildcard); + } + + let mut annotation = annotation.clone(); if !rigid_substitution.is_empty() { annotation.substitute(&rigid_substitution); } - if let Some(new_headers) = crate::pattern::headers_from_annotation( - &loc_pattern.value, - &Loc::at(loc_pattern.region, annotation.clone()), - ) { - for (symbol, loc_type) in new_headers { - for var in loc_type.value.variables() { - // a rigid is only new if this annotation is the first occurrence of this rigid - if !outside_rigids.contains(&var) { - new_rigids.push(var); - } - } - headers.insert(symbol, loc_type); - } - } - - for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { - ftv.insert(format!("*{}", i).into(), *wildcard); + let loc_annotation_ref = Loc::at(loc_pattern.region, &annotation); + if let Pattern::Identifier(symbol) = loc_pattern.value { + headers.insert(symbol, Loc::at(loc_pattern.region, annotation.clone())); + } else if let Some(new_headers) = + crate::pattern::headers_from_annotation(&loc_pattern.value, &loc_annotation_ref) + { + headers.extend(new_headers) } annotation @@ -1584,7 +1601,6 @@ pub fn rec_defs_help( def_pattern_state.vars.push(expr_var); - let mut new_rigids = Vec::new(); match &def.annotation { None => { let expr_con = constrain_expr( @@ -1611,6 +1627,7 @@ pub fn rec_defs_help( Some(annotation) => { let arity = annotation.signature.arity(); let mut ftv = env.rigids.clone(); + let mut new_rigids = Vec::new(); let signature = instantiate_rigids( &annotation.signature, diff --git a/compiler/constrain/src/pattern.rs b/compiler/constrain/src/pattern.rs index 200f38dc74..c0aee74614 100644 --- a/compiler/constrain/src/pattern.rs +++ b/compiler/constrain/src/pattern.rs @@ -27,7 +27,7 @@ pub struct PatternState { /// definition has an annotation, we instead now add `x => Int`. pub fn headers_from_annotation( pattern: &Pattern, - annotation: &Loc, + annotation: &Loc<&Type>, ) -> Option>> { let mut headers = SendMap::default(); // Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int` @@ -44,12 +44,13 @@ pub fn headers_from_annotation( fn headers_from_annotation_help( pattern: &Pattern, - annotation: &Loc, + annotation: &Loc<&Type>, headers: &mut SendMap>, ) -> bool { match pattern { Identifier(symbol) | Shadowed(_, _, symbol) => { - headers.insert(*symbol, annotation.clone()); + let typ = Loc::at(annotation.region, annotation.value.clone()); + headers.insert(*symbol, typ); true } Underscore @@ -106,7 +107,7 @@ fn headers_from_annotation_help( .all(|(arg_pattern, arg_type)| { headers_from_annotation_help( &arg_pattern.1.value, - &Loc::at(annotation.region, arg_type.clone()), + &Loc::at(annotation.region, arg_type), headers, ) }) @@ -135,12 +136,13 @@ fn headers_from_annotation_help( && type_arguments.len() == pat_type_arguments.len() && lambda_set_variables.len() == pat_lambda_set_variables.len() => { - headers.insert(*opaque, annotation.clone()); + let typ = Loc::at(annotation.region, annotation.value.clone()); + headers.insert(*opaque, typ); let (_, argument_pat) = &**argument; headers_from_annotation_help( &argument_pat.value, - &Loc::at(annotation.region, (**actual).clone()), + &Loc::at(annotation.region, actual), headers, ) } diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 6987ac4b4c..28566441d7 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -5509,4 +5509,42 @@ mod solve_expr { r#"Id [ A, B, C { a : Str }e ] -> Str"#, ) } + + #[test] + fn inner_annotation_rigid() { + infer_eq_without_problem( + indoc!( + r#" + f : a -> a + f = + g : b -> b + g = \x -> x + + g + + f + "# + ), + r#"a -> a"#, + ) + } + + #[test] + fn inner_annotation_rigid_2() { + infer_eq_without_problem( + indoc!( + r#" + f : {} -> List a + f = + g : List a + g = [] + + \{} -> g + + f + "# + ), + r#"{} -> List a"#, + ) + } } diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index 298fd32be8..c37e0f38ed 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -991,6 +991,118 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet) { } } +pub struct VariablesIter<'a> { + stack: Vec<&'a Type>, + recursion_variables: Vec, + variables: Vec, +} + +impl<'a> VariablesIter<'a> { + pub fn new(typ: &'a Type) -> Self { + Self { + stack: vec![typ], + recursion_variables: vec![], + variables: vec![], + } + } +} + +impl<'a> Iterator for VariablesIter<'a> { + type Item = Variable; + + fn next(&mut self) -> Option { + use Type::*; + + if let Some(var) = self.variables.pop() { + debug_assert!(!self.recursion_variables.contains(&var)); + + return Some(var); + } + + while let Some(tipe) = self.stack.pop() { + match tipe { + EmptyRec | EmptyTagUnion | Erroneous(_) => { + continue; + } + + ClosureTag { ext: v, .. } | Variable(v) => { + if !self.recursion_variables.contains(v) { + return Some(*v); + } + } + + Function(args, closure, ret) => { + self.stack.push(ret); + self.stack.push(closure); + self.stack.extend(args.iter().rev()); + } + Record(fields, ext) => { + use RecordField::*; + + self.stack.push(ext); + + for (_, field) in fields { + match field { + Optional(x) => self.stack.push(x), + Required(x) => self.stack.push(x), + Demanded(x) => self.stack.push(x), + }; + } + } + TagUnion(tags, ext) => { + self.stack.push(ext); + + for (_, args) in tags { + self.stack.extend(args); + } + } + FunctionOrTagUnion(_, _, ext) => { + self.stack.push(ext); + } + RecursiveTagUnion(rec, tags, ext) => { + self.recursion_variables.push(*rec); + self.stack.push(ext); + + for (_, args) in tags.iter().rev() { + self.stack.extend(args); + } + } + Alias { + type_arguments, + actual, + .. + } => { + self.stack.push(actual); + + for (_, args) in type_arguments.iter().rev() { + self.stack.push(args); + } + } + HostExposedAlias { + type_arguments: arguments, + actual, + .. + } => { + self.stack.push(actual); + + for (_, args) in arguments.iter().rev() { + self.stack.push(args); + } + } + RangedNumber(typ, vars) => { + self.stack.push(typ); + self.variables.extend(vars.iter().copied()); + } + Apply(_, args, _) => { + self.stack.extend(args); + } + } + } + + None + } +} + fn variables_help(tipe: &Type, accum: &mut ImSet) { use Type::*; From 39c4b878e5cf3dfe21428e25213987a8d6542dac Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 4 Mar 2022 23:27:05 +0100 Subject: [PATCH 103/132] remove comment --- compiler/constrain/src/expr.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 2d3d131019..07e2c4c1df 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1233,23 +1233,6 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint { let rigids = &env.rigids; let mut ftv = rigids.clone(); - /* - let mut new_rigids = Vec::new(); - - // pub wildcards: Vec, - // pub var_by_name: SendMap, - 'outer: for (outer_name, outer_var) in rigids.iter() { - for (inner_name, inner_var) in annotation.introduced_variables.var_by_name.iter() { - if outer_name == inner_name { - debug_assert_eq!(inner_var, outer_var); - continue 'outer; - } - } - - // the inner name is not in the outer scope; it's introduced here - } - */ - let signature = instantiate_rigids( &annotation.signature, &annotation.introduced_variables, From 1d3aa261931d0c8fbac6d8f40b66fe46f9408fec Mon Sep 17 00:00:00 2001 From: Pierre-Henri Trivier Date: Fri, 4 Mar 2022 23:35:53 +0100 Subject: [PATCH 104/132] Fixes #2586 --- compiler/fmt/src/annotation.rs | 3 +++ compiler/fmt/tests/test_fmt.rs | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/compiler/fmt/src/annotation.rs b/compiler/fmt/src/annotation.rs index 4380af17e2..4c389f7147 100644 --- a/compiler/fmt/src/annotation.rs +++ b/compiler/fmt/src/annotation.rs @@ -293,6 +293,9 @@ impl<'a> Formattable for TypeAnnotation<'a> { SpaceBefore(ann, spaces) => { buf.newline(); + + buf.indent(indent); + fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); ann.format_with_options(buf, parens, Newlines::No, indent) } diff --git a/compiler/fmt/tests/test_fmt.rs b/compiler/fmt/tests/test_fmt.rs index a49914abce..edea7957d3 100644 --- a/compiler/fmt/tests/test_fmt.rs +++ b/compiler/fmt/tests/test_fmt.rs @@ -2984,6 +2984,18 @@ mod test_fmt { )); } + #[test] + fn multiline_higher_order_function() { + expr_formats_same(indoc!( + r#" + foo : + (Str -> Bool) -> Bool + + 42 + "# + )); + } + #[test] /// Test that everything under examples/ is formatted correctly /// If this test fails on your diff, it probably means you need to re-format the examples. From 4ca9ea0b89669578a2e9697a5b9c98da87888090 Mon Sep 17 00:00:00 2001 From: Folkert Date: Fri, 4 Mar 2022 23:57:41 +0100 Subject: [PATCH 105/132] refactor After --- compiler/solve/src/solve.rs | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index c7ce79b7a6..9bedf114f7 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -211,19 +211,13 @@ pub fn run_in_place( state.env } -enum After { - CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), -} - enum Work<'a> { Constraint { env: &'a Env, rank: Rank, constraint: &'a Constraint, - after: Option, }, - /// Something to be done after a constraint and all its dependencies are fully solved. - After(After), + CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), } #[allow(clippy::too_many_arguments)] @@ -238,16 +232,16 @@ fn solve( subs: &mut Subs, constraint: &Constraint, ) -> State { - let mut stack = vec![Work::Constraint { + let initial = Work::Constraint { env, rank, constraint, - after: None, - }]; + }; + let mut stack = vec![initial]; while let Some(work_item) = stack.pop() { let (env, rank, constraint) = match work_item { - Work::After(After::CheckForInfiniteTypes(def_vars)) => { + Work::CheckForInfiniteTypes(def_vars) => { for (symbol, loc_var) in def_vars.iter() { check_for_infinite_type(subs, problems, *symbol, *loc_var); } @@ -258,15 +252,7 @@ fn solve( env, rank, constraint, - after, - } => { - // Push the `after` on first so that we look at it immediately after finishing all - // the children of this constraint. - if let Some(after) = after { - stack.push(Work::After(after)); - } - (env, rank, constraint) - } + } => (env, rank, constraint), }; state = match constraint { @@ -421,7 +407,6 @@ fn solve( env, rank, constraint: sub_constraint, - after: None, }) } @@ -483,7 +468,6 @@ fn solve( env, rank, constraint: &let_con.defs_constraint, - after: None, }); state } @@ -523,11 +507,11 @@ fn solve( new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); } + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); stack.push(Work::Constraint { env: arena.alloc(new_env), rank, constraint: ret_con, - after: Some(After::CheckForInfiniteTypes(local_def_vars)), }); state @@ -667,11 +651,11 @@ fn solve( // Now solve the body, using the new vars_by_symbol which includes // the assignments' name-to-variable mappings. + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); stack.push(Work::Constraint { env: arena.alloc(new_env), rank, constraint: ret_con, - after: Some(After::CheckForInfiniteTypes(local_def_vars)), }); state_for_ret_con From 41df04184e10f986988ed71eb8a90028bf0cf2ca Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 00:26:03 +0100 Subject: [PATCH 106/132] make the simple case tail-recursive --- compiler/solve/src/solve.rs | 89 ++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 9bedf114f7..080a693493 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -1,6 +1,6 @@ use bumpalo::Bump; use roc_can::constraint::Constraint::{self, *}; -use roc_can::constraint::PresenceConstraint; +use roc_can::constraint::{LetConstraint, PresenceConstraint}; use roc_can::expected::{Expected, PExpected}; use roc_collections::all::MutMap; use roc_module::ident::TagName; @@ -218,6 +218,11 @@ enum Work<'a> { constraint: &'a Constraint, }, CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), + LetConSimple { + env: &'a Env, + rank: Rank, + let_con: &'a LetConstraint, + }, } #[allow(clippy::too_many_arguments)] @@ -241,6 +246,11 @@ fn solve( let mut stack = vec![initial]; while let Some(work_item) = stack.pop() { let (env, rank, constraint) = match work_item { + Work::Constraint { + env, + rank, + constraint, + } => (env, rank, constraint), Work::CheckForInfiniteTypes(def_vars) => { for (symbol, loc_var) in def_vars.iter() { check_for_infinite_type(subs, problems, *symbol, *loc_var); @@ -248,11 +258,35 @@ fn solve( // No constraint to be solved continue; } - Work::Constraint { - env, - rank, - constraint, - } => (env, rank, constraint), + Work::LetConSimple { env, rank, let_con } => { + // Add a variable for each def to new_vars_by_env. + let mut local_def_vars = LocalDefVarsVec::with_length(let_con.def_types.len()); + + for (symbol, loc_type) in let_con.def_types.iter() { + let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); + + local_def_vars.push(( + *symbol, + Loc { + value: var, + region: loc_type.region, + }, + )); + } + + let mut new_env = env.clone(); + for (symbol, loc_var) in local_def_vars.iter() { + new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); + } + + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); + stack.push(Work::Constraint { + env: arena.alloc(new_env), + rank, + constraint: &let_con.ret_constraint, + }); + continue; + } }; state = match constraint { @@ -471,47 +505,12 @@ fn solve( }); state } - ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { - // TODO: make into `WorkItem` with `After` - let state = solve( - arena, - env, - state, - rank, - pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ); - - // Add a variable for each def to new_vars_by_env. - let mut local_def_vars = - LocalDefVarsVec::with_length(let_con.def_types.len()); - - for (symbol, loc_type) in let_con.def_types.iter() { - let var = - type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); - - local_def_vars.push(( - *symbol, - Loc { - value: var, - region: loc_type.region, - }, - )); - } - - let mut new_env = env.clone(); - for (symbol, loc_var) in local_def_vars.iter() { - new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); - } - - stack.push(Work::CheckForInfiniteTypes(local_def_vars)); + _ if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { + stack.push(Work::LetConSimple { env, rank, let_con }); stack.push(Work::Constraint { - env: arena.alloc(new_env), + env, rank, - constraint: ret_con, + constraint: &let_con.defs_constraint, }); state From 3a1add6ce81652d0a1df1800f116d037047189d6 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 00:51:08 +0100 Subject: [PATCH 107/132] add LocalDefVarsVec::from_def_types --- compiler/solve/src/solve.rs | 71 +++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 080a693493..d8538383ea 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -2,7 +2,7 @@ use bumpalo::Bump; use roc_can::constraint::Constraint::{self, *}; use roc_can::constraint::{LetConstraint, PresenceConstraint}; use roc_can::expected::{Expected, PExpected}; -use roc_collections::all::MutMap; +use roc_collections::all::{MutMap, SendMap}; use roc_module::ident::TagName; use roc_module::symbol::Symbol; use roc_region::all::{Loc, Region}; @@ -260,19 +260,13 @@ fn solve( } Work::LetConSimple { env, rank, let_con } => { // Add a variable for each def to new_vars_by_env. - let mut local_def_vars = LocalDefVarsVec::with_length(let_con.def_types.len()); - - for (symbol, loc_type) in let_con.def_types.iter() { - let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); - - local_def_vars.push(( - *symbol, - Loc { - value: var, - region: loc_type.region, - }, - )); - } + let local_def_vars = LocalDefVarsVec::from_def_types( + rank, + pools, + cached_aliases, + subs, + &let_con.def_types, + ); let mut new_env = env.clone(); for (symbol, loc_var) in local_def_vars.iter() { @@ -548,22 +542,13 @@ fn solve( // run solver in next pool // Add a variable for each def to local_def_vars. - let mut local_def_vars = - LocalDefVarsVec::with_length(let_con.def_types.len()); - - for (symbol, loc_type) in let_con.def_types.iter() { - let def_type = &loc_type.value; - - let var = type_to_var(subs, next_rank, pools, cached_aliases, def_type); - - local_def_vars.push(( - *symbol, - Loc { - value: var, - region: loc_type.region, - }, - )); - } + let local_def_vars = LocalDefVarsVec::from_def_types( + next_rank, + pools, + cached_aliases, + subs, + &let_con.def_types, + ); // Solve the assignments' constraints first. // TODO: make into `WorkItem` with `After` @@ -759,6 +744,32 @@ impl LocalDefVarsVec { } } +impl LocalDefVarsVec<(Symbol, Loc)> { + fn from_def_types( + rank: Rank, + pools: &mut Pools, + cached_aliases: &mut MutMap, + subs: &mut Subs, + def_types: &SendMap>, + ) -> Self { + let mut local_def_vars = Self::with_length(def_types.len()); + + for (symbol, loc_type) in def_types.iter() { + let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); + + local_def_vars.push(( + *symbol, + Loc { + value: var, + region: loc_type.region, + }, + )); + } + + local_def_vars + } +} + use std::cell::RefCell; std::thread_local! { /// Scratchpad arena so we don't need to allocate a new one all the time From c7d8ae6c798be447ec8ec4e5919f29c8cdb7a938 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 01:02:27 +0100 Subject: [PATCH 108/132] make complex case tail-recursive --- compiler/solve/src/solve.rs | 200 ++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 90 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index d8538383ea..3a4f20b277 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -223,6 +223,13 @@ enum Work<'a> { rank: Rank, let_con: &'a LetConstraint, }, + LetConComplex { + env: &'a Env, + rank: Rank, + next_rank: Rank, + let_con: &'a LetConstraint, + local_def_vars: LocalDefVarsVec<(Symbol, Loc)>, + }, } #[allow(clippy::too_many_arguments)] @@ -259,6 +266,8 @@ fn solve( continue; } Work::LetConSimple { env, rank, let_con } => { + // NOTE be extremely careful with shadowing here + // Add a variable for each def to new_vars_by_env. let local_def_vars = LocalDefVarsVec::from_def_types( rank, @@ -281,6 +290,96 @@ fn solve( }); continue; } + Work::LetConComplex { + env, + rank, + next_rank, + let_con, + local_def_vars, + } => { + // NOTE be extremely careful with shadowing here + let mark = state.mark; + let saved_env = state.env; + + let rigid_vars = &let_con.rigid_vars; + + let young_mark = mark; + let visit_mark = young_mark.next(); + let final_mark = visit_mark.next(); + + debug_assert_eq!( + { + let offenders = pools + .get(next_rank) + .iter() + .filter(|var| { + let current_rank = + subs.get_rank(roc_types::subs::Variable::clone(var)); + + current_rank.into_usize() > next_rank.into_usize() + }) + .collect::>(); + + let result = offenders.len(); + + if result > 0 { + dbg!(&subs, &offenders, &let_con.def_types); + } + + result + }, + 0 + ); + + // pop pool + generalize(subs, young_mark, visit_mark, next_rank, pools); + + pools.get_mut(next_rank).clear(); + + // check that things went well + debug_assert!({ + // NOTE the `subs.redundant` check is added for the uniqueness + // inference, and does not come from elm. It's unclear whether this is + // a bug with uniqueness inference (something is redundant that + // shouldn't be) or that it just never came up in elm. + let failing: Vec<_> = rigid_vars + .iter() + .filter(|&var| !subs.redundant(*var) && subs.get_rank(*var) != Rank::NONE) + .collect(); + + if !failing.is_empty() { + println!("Rigids {:?}", &rigid_vars); + println!("Failing {:?}", failing); + } + + failing.is_empty() + }); + + let mut new_env = env.clone(); + for (symbol, loc_var) in local_def_vars.iter() { + new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); + } + + // Note that this vars_by_symbol is the one returned by the + // previous call to solve() + let state_for_ret_con = State { + env: saved_env, + mark: final_mark, + }; + + // Now solve the body, using the new vars_by_symbol which includes + // the assignments' name-to-variable mappings. + stack.push(Work::CheckForInfiniteTypes(local_def_vars)); + stack.push(Work::Constraint { + env: arena.alloc(new_env), + rank, + constraint: &let_con.ret_constraint, + }); + + state = state_for_ret_con; + + continue; + } }; state = match constraint { @@ -509,7 +608,7 @@ fn solve( state } - ret_con => { + _ => { let rigid_vars = &let_con.rigid_vars; let flex_vars = &let_con.flex_vars; @@ -550,99 +649,20 @@ fn solve( &let_con.def_types, ); - // Solve the assignments' constraints first. - // TODO: make into `WorkItem` with `After` - let State { - env: saved_env, - mark, - } = solve( - arena, + stack.push(Work::LetConComplex { env, - state, - next_rank, - pools, - problems, - cached_aliases, - subs, - &let_con.defs_constraint, - ); - - let young_mark = mark; - let visit_mark = young_mark.next(); - let final_mark = visit_mark.next(); - - debug_assert_eq!( - { - let offenders = pools - .get(next_rank) - .iter() - .filter(|var| { - let current_rank = - subs.get_rank(roc_types::subs::Variable::clone(var)); - - current_rank.into_usize() > next_rank.into_usize() - }) - .collect::>(); - - let result = offenders.len(); - - if result > 0 { - dbg!(&subs, &offenders, &let_con.def_types); - } - - result - }, - 0 - ); - - // pop pool - generalize(subs, young_mark, visit_mark, next_rank, pools); - - pools.get_mut(next_rank).clear(); - - // check that things went well - debug_assert!({ - // NOTE the `subs.redundant` check is added for the uniqueness - // inference, and does not come from elm. It's unclear whether this is - // a bug with uniqueness inference (something is redundant that - // shouldn't be) or that it just never came up in elm. - let failing: Vec<_> = rigid_vars - .iter() - .filter(|&var| { - !subs.redundant(*var) && subs.get_rank(*var) != Rank::NONE - }) - .collect(); - - if !failing.is_empty() { - println!("Rigids {:?}", &rigid_vars); - println!("Failing {:?}", failing); - } - - failing.is_empty() - }); - - let mut new_env = env.clone(); - for (symbol, loc_var) in local_def_vars.iter() { - new_env.insert_symbol_var_if_vacant(*symbol, loc_var.value); - } - - // Note that this vars_by_symbol is the one returned by the - // previous call to solve() - let state_for_ret_con = State { - env: saved_env, - mark: final_mark, - }; - - // Now solve the body, using the new vars_by_symbol which includes - // the assignments' name-to-variable mappings. - stack.push(Work::CheckForInfiniteTypes(local_def_vars)); - stack.push(Work::Constraint { - env: arena.alloc(new_env), rank, - constraint: ret_con, + let_con, + local_def_vars, + next_rank, + }); + stack.push(Work::Constraint { + env, + rank: next_rank, + constraint: &let_con.defs_constraint, }); - state_for_ret_con + state } } } From a79f6c6cdd11622867ce91a065e3fcf57ebed712 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 01:29:21 +0100 Subject: [PATCH 109/132] restructure and add comments --- compiler/solve/src/solve.rs | 169 ++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 86 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 3a4f20b277..24fd26ca1a 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -226,9 +226,7 @@ enum Work<'a> { LetConComplex { env: &'a Env, rank: Rank, - next_rank: Rank, let_con: &'a LetConstraint, - local_def_vars: LocalDefVarsVec<(Symbol, Loc)>, }, } @@ -257,12 +255,17 @@ fn solve( env, rank, constraint, - } => (env, rank, constraint), + } => { + // the default case; actually solve this constraint + (env, rank, constraint) + } Work::CheckForInfiniteTypes(def_vars) => { + // after a LetCon, we must check if any of the variables that we introduced + // loop back to themselves after solving the ret_constraint for (symbol, loc_var) in def_vars.iter() { check_for_infinite_type(subs, problems, *symbol, *loc_var); } - // No constraint to be solved + continue; } Work::LetConSimple { env, rank, let_con } => { @@ -288,16 +291,13 @@ fn solve( rank, constraint: &let_con.ret_constraint, }); + continue; } - Work::LetConComplex { - env, - rank, - next_rank, - let_con, - local_def_vars, - } => { + Work::LetConComplex { env, rank, let_con } => { // NOTE be extremely careful with shadowing here + let next_rank = rank.next(); + let mark = state.mark; let saved_env = state.env; @@ -307,16 +307,22 @@ fn solve( let visit_mark = young_mark.next(); let final_mark = visit_mark.next(); + // Add a variable for each def to local_def_vars. + let local_def_vars = LocalDefVarsVec::from_def_types( + next_rank, + pools, + cached_aliases, + subs, + &let_con.def_types, + ); + debug_assert_eq!( { let offenders = pools .get(next_rank) .iter() .filter(|var| { - let current_rank = - subs.get_rank(roc_types::subs::Variable::clone(var)); - - current_rank.into_usize() > next_rank.into_usize() + subs.get_rank(**var).into_usize() > next_rank.into_usize() }) .collect::>(); @@ -385,7 +391,6 @@ fn solve( state = match constraint { True => state, SaveTheEnvironment => { - // NOTE deviation: elm only copies the env into the state on SaveTheEnvironment let mut copy = state; copy.env = env.clone(); @@ -585,85 +590,77 @@ fn solve( } } Let(let_con) => { - match &let_con.ret_constraint { - True if let_con.rigid_vars.is_empty() => { - introduce(subs, rank, pools, &let_con.flex_vars); + if matches!(&let_con.ret_constraint, True) && let_con.rigid_vars.is_empty() { + introduce(subs, rank, pools, &let_con.flex_vars); - // If the return expression is guaranteed to solve, - // solve the assignments themselves and move on. - stack.push(Work::Constraint { - env, - rank, - constraint: &let_con.defs_constraint, - }); - state + // If the return expression is guaranteed to solve, + // solve the assignments themselves and move on. + stack.push(Work::Constraint { + env, + rank, + constraint: &let_con.defs_constraint, + }); + + state + } else if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() { + // items are popped from the stack in reverse order. That means that we'll + // first solve then defs_constraint, and then (eventually) the ret_constraint. + // + // Note that the LetConSimple gets the current env and rank, + // and not the env/rank from after solving the defs_constraint + stack.push(Work::LetConSimple { env, rank, let_con }); + stack.push(Work::Constraint { + env, + rank, + constraint: &let_con.defs_constraint, + }); + + state + } else { + let rigid_vars = &let_con.rigid_vars; + let flex_vars = &let_con.flex_vars; + + // work in the next pool to localize header + let next_rank = rank.next(); + + // introduce variables + for &var in rigid_vars.iter().chain(flex_vars.iter()) { + subs.set_rank(var, next_rank); } - _ if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { - stack.push(Work::LetConSimple { env, rank, let_con }); - stack.push(Work::Constraint { - env, - rank, - constraint: &let_con.defs_constraint, - }); - state + // determine the next pool + if next_rank.into_usize() < pools.len() { + // Nothing to do, we already accounted for the next rank, no need to + // adjust the pools + } else { + // we should be off by one at this point + debug_assert_eq!(next_rank.into_usize(), 1 + pools.len()); + pools.extend_to(next_rank.into_usize()); } - _ => { - let rigid_vars = &let_con.rigid_vars; - let flex_vars = &let_con.flex_vars; - // work in the next pool to localize header - let next_rank = rank.next(); + let pool: &mut Vec = pools.get_mut(next_rank); - // introduce variables - for &var in rigid_vars.iter().chain(flex_vars.iter()) { - subs.set_rank(var, next_rank); - } + // Replace the contents of this pool with rigid_vars and flex_vars + pool.clear(); + pool.reserve(rigid_vars.len() + flex_vars.len()); + pool.extend(rigid_vars.iter()); + pool.extend(flex_vars.iter()); - // determine the next pool - if next_rank.into_usize() < pools.len() { - // Nothing to do, we already accounted for the next rank, no need to - // adjust the pools - } else { - // we should be off by one at this point - debug_assert_eq!(next_rank.into_usize(), 1 + pools.len()); - pools.extend_to(next_rank.into_usize()); - } + // run solver in next pool - let pool: &mut Vec = pools.get_mut(next_rank); + // items are popped from the stack in reverse order. That means that we'll + // first solve then defs_constraint, and then (eventually) the ret_constraint. + // + // Note that the LetConSimple gets the current env and rank, + // and not the env/rank from after solving the defs_constraint + stack.push(Work::LetConComplex { env, rank, let_con }); + stack.push(Work::Constraint { + env, + rank: next_rank, + constraint: &let_con.defs_constraint, + }); - // Replace the contents of this pool with rigid_vars and flex_vars - pool.clear(); - pool.reserve(rigid_vars.len() + flex_vars.len()); - pool.extend(rigid_vars.iter()); - pool.extend(flex_vars.iter()); - - // run solver in next pool - - // Add a variable for each def to local_def_vars. - let local_def_vars = LocalDefVarsVec::from_def_types( - next_rank, - pools, - cached_aliases, - subs, - &let_con.def_types, - ); - - stack.push(Work::LetConComplex { - env, - rank, - let_con, - local_def_vars, - next_rank, - }); - stack.push(Work::Constraint { - env, - rank: next_rank, - constraint: &let_con.defs_constraint, - }); - - state - } + state } } Present(typ, PresenceConstraint::IsOpen) => { From 241dc5783e8bf6b8cb87273bfac9130ef8a0750c Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 01:37:23 +0100 Subject: [PATCH 110/132] remove VariablesIter, it was unused --- compiler/solve/tests/solve_expr.rs | 38 ---------- compiler/types/src/types.rs | 112 ----------------------------- 2 files changed, 150 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 28566441d7..6987ac4b4c 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -5509,42 +5509,4 @@ mod solve_expr { r#"Id [ A, B, C { a : Str }e ] -> Str"#, ) } - - #[test] - fn inner_annotation_rigid() { - infer_eq_without_problem( - indoc!( - r#" - f : a -> a - f = - g : b -> b - g = \x -> x - - g - - f - "# - ), - r#"a -> a"#, - ) - } - - #[test] - fn inner_annotation_rigid_2() { - infer_eq_without_problem( - indoc!( - r#" - f : {} -> List a - f = - g : List a - g = [] - - \{} -> g - - f - "# - ), - r#"{} -> List a"#, - ) - } } diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index c37e0f38ed..298fd32be8 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -991,118 +991,6 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet) { } } -pub struct VariablesIter<'a> { - stack: Vec<&'a Type>, - recursion_variables: Vec, - variables: Vec, -} - -impl<'a> VariablesIter<'a> { - pub fn new(typ: &'a Type) -> Self { - Self { - stack: vec![typ], - recursion_variables: vec![], - variables: vec![], - } - } -} - -impl<'a> Iterator for VariablesIter<'a> { - type Item = Variable; - - fn next(&mut self) -> Option { - use Type::*; - - if let Some(var) = self.variables.pop() { - debug_assert!(!self.recursion_variables.contains(&var)); - - return Some(var); - } - - while let Some(tipe) = self.stack.pop() { - match tipe { - EmptyRec | EmptyTagUnion | Erroneous(_) => { - continue; - } - - ClosureTag { ext: v, .. } | Variable(v) => { - if !self.recursion_variables.contains(v) { - return Some(*v); - } - } - - Function(args, closure, ret) => { - self.stack.push(ret); - self.stack.push(closure); - self.stack.extend(args.iter().rev()); - } - Record(fields, ext) => { - use RecordField::*; - - self.stack.push(ext); - - for (_, field) in fields { - match field { - Optional(x) => self.stack.push(x), - Required(x) => self.stack.push(x), - Demanded(x) => self.stack.push(x), - }; - } - } - TagUnion(tags, ext) => { - self.stack.push(ext); - - for (_, args) in tags { - self.stack.extend(args); - } - } - FunctionOrTagUnion(_, _, ext) => { - self.stack.push(ext); - } - RecursiveTagUnion(rec, tags, ext) => { - self.recursion_variables.push(*rec); - self.stack.push(ext); - - for (_, args) in tags.iter().rev() { - self.stack.extend(args); - } - } - Alias { - type_arguments, - actual, - .. - } => { - self.stack.push(actual); - - for (_, args) in type_arguments.iter().rev() { - self.stack.push(args); - } - } - HostExposedAlias { - type_arguments: arguments, - actual, - .. - } => { - self.stack.push(actual); - - for (_, args) in arguments.iter().rev() { - self.stack.push(args); - } - } - RangedNumber(typ, vars) => { - self.stack.push(typ); - self.variables.extend(vars.iter().copied()); - } - Apply(_, args, _) => { - self.stack.extend(args); - } - } - } - - None - } -} - fn variables_help(tipe: &Type, accum: &mut ImSet) { use Type::*; From 76b4e7ec2e56f48f697490769eabcb40a433bae7 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Fri, 4 Mar 2022 19:46:52 -0500 Subject: [PATCH 111/132] Add Derek, Philippe, and Pierre-Henri to AUTHORS --- AUTHORS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS b/AUTHORS index 58357ab2bd..bdacbd757f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -65,3 +65,6 @@ Mats Sigge <> Drew Lazzeri Tom Dohrmann Elijah Schow +Derek Gustafson +Philippe Vinchon +Pierre-Henri Trivier From 8c321c1aa499ddf0dff45ac69d2fd2f171fded60 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 02:12:10 +0100 Subject: [PATCH 112/132] fix a bug with taking a scratchpad arena --- compiler/solve/src/solve.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index c7ce79b7a6..a8e17b3d4f 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -370,7 +370,8 @@ fn solve( // then we copy from that module's Subs into our own. If the value // is being looked up in this module, then we use our Subs as both // the source and destination. - let actual = deep_copy_var(subs, rank, pools, var); + let actual = deep_copy_var_in(subs, rank, pools, var, arena); + let expected = type_to_var( subs, rank, @@ -779,21 +780,16 @@ impl LocalDefVarsVec { use std::cell::RefCell; std::thread_local! { /// Scratchpad arena so we don't need to allocate a new one all the time - static SCRATCHPAD: RefCell = RefCell::new(bumpalo::Bump::with_capacity(4 * 1024)); + static SCRATCHPAD: RefCell> = RefCell::new(Some(bumpalo::Bump::with_capacity(4 * 1024))); } fn take_scratchpad() -> bumpalo::Bump { - let mut result = bumpalo::Bump::new(); - SCRATCHPAD.with(|f| { - result = f.replace(bumpalo::Bump::new()); - }); - - result + SCRATCHPAD.with(|f| f.take().unwrap()) } fn put_scratchpad(scratchpad: bumpalo::Bump) { SCRATCHPAD.with(|f| { - f.replace(scratchpad); + f.replace(Some(scratchpad)); }); } @@ -976,7 +972,7 @@ fn type_to_variable<'a>( return reserved; } else { // for any other rank, we need to copy; it takes care of adjusting the rank - return deep_copy_var(subs, rank, pools, reserved); + return deep_copy_var_in(subs, rank, pools, reserved, arena); } } @@ -1044,6 +1040,7 @@ fn type_to_variable<'a>( } } +#[inline(always)] fn alias_to_var<'a>( subs: &mut Subs, rank: Rank, @@ -1073,6 +1070,7 @@ fn alias_to_var<'a>( } } +#[inline(always)] fn roc_result_to_var<'a>( subs: &mut Subs, rank: Rank, @@ -1821,10 +1819,14 @@ fn instantiate_rigids_help(subs: &mut Subs, max_rank: Rank, initial: Variable) { } } -fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) -> Variable { - let mut arena = take_scratchpad(); - - let mut visited = bumpalo::collections::Vec::with_capacity_in(4 * 1024, &arena); +fn deep_copy_var_in( + subs: &mut Subs, + rank: Rank, + pools: &mut Pools, + var: Variable, + arena: &Bump, +) -> Variable { + let mut visited = bumpalo::collections::Vec::with_capacity_in(256, arena); let copy = deep_copy_var_help(subs, rank, pools, &mut visited, var); @@ -1840,9 +1842,6 @@ fn deep_copy_var(subs: &mut Subs, rank: Rank, pools: &mut Pools, var: Variable) } } - arena.reset(); - put_scratchpad(arena); - copy } @@ -2081,6 +2080,7 @@ fn deep_copy_var_help( } } +#[inline(always)] fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) -> Variable { let descriptor = Descriptor { content, From cbac0cf318ea065518b2645b67eb24d7802a7aef Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 5 Mar 2022 09:32:37 +0100 Subject: [PATCH 113/132] removed redundant roc_file.rs --- ast/src/roc_file.rs | 133 -------------------------------------------- 1 file changed, 133 deletions(-) delete mode 100644 ast/src/roc_file.rs diff --git a/ast/src/roc_file.rs b/ast/src/roc_file.rs deleted file mode 100644 index 5299921c62..0000000000 --- a/ast/src/roc_file.rs +++ /dev/null @@ -1,133 +0,0 @@ -use bumpalo::collections::Vec; -use bumpalo::Bump; -use roc_fmt::def::fmt_def; -use roc_fmt::module::fmt_module; -use roc_parse::ast::{Def, Module}; -use roc_parse::module::module_defs; -use roc_parse::parser; -use roc_parse::parser::{Parser, SyntaxError}; -use roc_region::all::Located; -use std::ffi::OsStr; -use std::path::Path; -use std::{fs, io}; - -#[derive(Debug)] -pub struct File<'a> { - path: &'a Path, - module_header: Module<'a>, - content: Vec<'a, Located>>, -} - -#[derive(Debug)] -pub enum ReadError<'a> { - Read(std::io::Error), - ParseDefs(SyntaxError<'a>), - ParseHeader(SyntaxError<'a>), - DoesntHaveRocExtension, -} - -impl<'a> File<'a> { - pub fn read(path: &'a Path, arena: &'a Bump) -> Result, ReadError<'a>> { - if path.extension() != Some(OsStr::new("roc")) { - return Err(ReadError::DoesntHaveRocExtension); - } - - let bytes = fs::read(path).map_err(ReadError::Read)?; - - let allocation = arena.alloc(bytes); - - let module_parse_state = parser::State::new(allocation); - let parsed_module = roc_parse::module::parse_header(arena, module_parse_state); - - match parsed_module { - Ok((module, state)) => { - let parsed_defs = module_defs().parse(arena, state); - - match parsed_defs { - Ok((_, defs, _)) => Ok(File { - path, - module_header: module, - content: defs, - }), - Err((_, error, _)) => Err(ReadError::ParseDefs(error)), - } - } - Err(error) => Err(ReadError::ParseHeader(SyntaxError::Header(error))), - } - } - - pub fn fmt(&self) -> String { - let arena = Bump::new(); - let mut formatted_file = String::new(); - - let mut module_header_buf = bumpalo::collections::String::new_in(&arena); - fmt_module(&mut module_header_buf, &self.module_header); - - formatted_file.push_str(module_header_buf.as_str()); - - for def in &self.content { - let mut def_buf = bumpalo::collections::String::new_in(&arena); - - fmt_def(&mut def_buf, &def.value, 0); - - formatted_file.push_str(def_buf.as_str()); - } - - formatted_file - } - - pub fn fmt_then_write_to(&self, write_path: &'a Path) -> io::Result<()> { - let formatted_file = self.fmt(); - - fs::write(write_path, formatted_file) - } - - pub fn fmt_then_write_with_name(&self, new_name: &str) -> io::Result<()> { - self.fmt_then_write_to( - self.path - .with_file_name(new_name) - .with_extension("roc") - .as_path(), - ) - } - - pub fn fmt_then_write(&self) -> io::Result<()> { - self.fmt_then_write_to(self.path) - } -} - -#[cfg(test)] -mod test_file { - use crate::lang::roc_file; - use bumpalo::Bump; - use std::path::Path; - - #[test] - fn read_and_fmt_simple_roc_module() { - let simple_module_path = Path::new("./tests/modules/SimpleUnformatted.roc"); - - let arena = Bump::new(); - - let file = roc_file::File::read(simple_module_path, &arena) - .expect("Could not read SimpleUnformatted.roc in test_file test"); - - assert_eq!( - file.fmt(), - indoc!( - r#" - interface Simple - exposes [ - v, x - ] - imports [] - - v : Str - - v = "Value!" - - x : Int - x = 4"# - ) - ); - } -} From 9302f2ca5e10a65b88de2d7e472de4d0b38814a9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 13:16:41 +0100 Subject: [PATCH 114/132] partially revert to working state --- compiler/constrain/src/expr.rs | 40 +++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 58bcf0d304..848009f399 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1639,9 +1639,11 @@ fn instantiate_rigids( loc_pattern: &Loc, headers: &mut SendMap>, ) -> Type { - // find out if rigid type variables first occur in this annotation, - // or if they are already introduced in an outer annotation + let mut annotation = annotation.clone(); let mut rigid_substitution: ImMap = ImMap::default(); + + let outside_rigids: Vec = ftv.values().copied().collect(); + for (name, var) in introduced_vars.var_by_name.iter() { use std::collections::hash_map::Entry::*; @@ -1653,29 +1655,33 @@ fn instantiate_rigids( Vacant(vacant) => { // It's possible to use this rigid in nested defs vacant.insert(*var); - new_rigids.push(*var); + // new_rigids.push(*var); } } } - // wildcards are always freshly introduced in this annotation - for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { - ftv.insert(format!("*{}", i).into(), *wildcard); - new_rigids.push(*wildcard); - } - - let mut annotation = annotation.clone(); + // Instantiate rigid variables if !rigid_substitution.is_empty() { annotation.substitute(&rigid_substitution); } - let loc_annotation_ref = Loc::at(loc_pattern.region, &annotation); - if let Pattern::Identifier(symbol) = loc_pattern.value { - headers.insert(symbol, Loc::at(loc_pattern.region, annotation.clone())); - } else if let Some(new_headers) = - crate::pattern::headers_from_annotation(&loc_pattern.value, &loc_annotation_ref) - { - headers.extend(new_headers) + if let Some(new_headers) = crate::pattern::headers_from_annotation( + &loc_pattern.value, + &Loc::at(loc_pattern.region, &annotation), + ) { + for (symbol, loc_type) in new_headers { + for var in loc_type.value.variables() { + // a rigid is only new if this annotation is the first occurrence of this rigid + if !outside_rigids.contains(&var) { + new_rigids.push(var); + } + } + headers.insert(symbol, loc_type); + } + } + + for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { + ftv.insert(format!("*{}", i).into(), *wildcard); } annotation From d7eee71402ac8f1b5f22bc28e580dd4eb6a2f2df Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 13:47:01 +0100 Subject: [PATCH 115/132] track lambda set variables; only use annotation variables in annotations... --- compiler/can/src/annotation.rs | 10 +++++++++- compiler/can/src/def.rs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index de0812c4a2..160ccd834f 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -29,6 +29,7 @@ pub struct IntroducedVariables { // but a variable can only have one name. Therefore // `ftv : SendMap`. pub wildcards: Vec, + pub lambda_sets: Vec, pub var_by_name: SendMap, pub name_by_var: SendMap, pub host_exposed_aliases: MutMap, @@ -44,12 +45,17 @@ impl IntroducedVariables { self.wildcards.push(var); } + fn insert_lambda_set(&mut self, var: Variable) { + self.lambda_sets.push(var); + } + pub fn insert_host_exposed_alias(&mut self, symbol: Symbol, var: Variable) { self.host_exposed_aliases.insert(symbol, var); } pub fn union(&mut self, other: &Self) { self.wildcards.extend(other.wildcards.iter().cloned()); + self.lambda_sets.extend(other.lambda_sets.iter().cloned()); self.var_by_name.extend(other.var_by_name.clone()); self.name_by_var.extend(other.name_by_var.clone()); self.host_exposed_aliases @@ -280,7 +286,9 @@ fn can_annotation_help( references, ); - let closure = Type::Variable(var_store.fresh()); + let lambda_set = var_store.fresh(); + introduced_variables.insert_lambda_set(lambda_set); + let closure = Type::Variable(lambda_set); Type::Function(args, Box::new(closure), Box::new(ret)) } diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 4f67ecda91..3916c17283 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1147,7 +1147,7 @@ fn canonicalize_pending_def<'a>( pattern_vars: vars_by_symbol.clone(), annotation: Some(Annotation { signature: typ.clone(), - introduced_variables: output.introduced_variables.clone(), + introduced_variables: ann.introduced_variables.clone(), aliases: ann.aliases.clone(), region: loc_ann.region, }), From 7c83e940bdb0976afb66d11dd6bda80e3f37e6e1 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 13:55:30 +0100 Subject: [PATCH 116/132] leave a TODO --- compiler/can/src/def.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 3916c17283..6411441d98 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1112,6 +1112,12 @@ fn canonicalize_pending_def<'a>( arguments: arguments.clone(), loc_body: body.clone(), }); + + // TODO exploit this fact to remove clones below + debug_assert_eq!( + vec![*defined_symbol], + scope.idents().map(|t| t.1 .0).filter(|x| vars_by_symbol.contains_key(x)).collect::>() + ); } // Store the referenced locals in the refs_by_symbol map, so we can later figure out From bcafc6612e2969a087d791a1a40a0bd5a12fee2e Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sat, 5 Mar 2022 08:07:25 -0500 Subject: [PATCH 117/132] Update Pierre-Henri's email in AUTHORS As requested in https://github.com/rtfeldman/roc/commit/76b4e7ec2e56f48f697490769eabcb40a433bae7#commitcomment-68027841 --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index bdacbd757f..a52e26ba97 100644 --- a/AUTHORS +++ b/AUTHORS @@ -67,4 +67,4 @@ Tom Dohrmann Elijah Schow Derek Gustafson Philippe Vinchon -Pierre-Henri Trivier +Pierre-Henri Trivier From 012a2d07a6accc072412113a830329fe5c3edea2 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 14:27:45 +0100 Subject: [PATCH 118/132] add Inferred variables to IntroducedVariables --- compiler/can/src/annotation.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 160ccd834f..67a248239e 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -30,6 +30,7 @@ pub struct IntroducedVariables { // `ftv : SendMap`. pub wildcards: Vec, pub lambda_sets: Vec, + pub inferred: Vec, pub var_by_name: SendMap, pub name_by_var: SendMap, pub host_exposed_aliases: MutMap, @@ -45,6 +46,10 @@ impl IntroducedVariables { self.wildcards.push(var); } + pub fn insert_inferred(&mut self, var: Variable) { + self.inferred.push(var); + } + fn insert_lambda_set(&mut self, var: Variable) { self.lambda_sets.push(var); } @@ -56,6 +61,7 @@ impl IntroducedVariables { pub fn union(&mut self, other: &Self) { self.wildcards.extend(other.wildcards.iter().cloned()); self.lambda_sets.extend(other.lambda_sets.iter().cloned()); + self.inferred.extend(other.inferred.iter().cloned()); self.var_by_name.extend(other.var_by_name.clone()); self.name_by_var.extend(other.name_by_var.clone()); self.host_exposed_aliases @@ -620,6 +626,9 @@ fn can_annotation_help( // Inference variables aren't bound to a rigid or a wildcard, so all we have to do is // make a fresh unconstrained variable, and let the type solver fill it in for us 🤠 let var = var_store.fresh(); + + introduced_variables.insert_inferred(var); + Type::Variable(var) } Malformed(string) => { From 6370a80c621ec2a2ad1458acf59148659b48bc9d Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 14:29:34 +0100 Subject: [PATCH 119/132] make sure lambda sets within aliases are in IntroducedVariables --- compiler/can/src/annotation.rs | 7 +++++++ compiler/solve/tests/solve_expr.rs | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 67a248239e..6a0052d4a9 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -340,6 +340,7 @@ fn can_annotation_help( let (type_arguments, lambda_set_variables, actual) = instantiate_and_freshen_alias_type( var_store, + introduced_variables, &alias.type_variables, args, &alias.lambda_set_variables, @@ -645,6 +646,7 @@ fn can_annotation_help( pub fn instantiate_and_freshen_alias_type( var_store: &mut VarStore, + introduced_variables: &mut IntroducedVariables, type_variables: &[Loc<(Lowercase, Variable)>], type_arguments: Vec, lambda_set_variables: &[LambdaSet], @@ -674,6 +676,7 @@ pub fn instantiate_and_freshen_alias_type( if let Type::Variable(var) = typ.0 { let fresh = var_store.fresh(); substitutions.insert(var, Type::Variable(fresh)); + introduced_variables.insert_lambda_set(fresh); new_lambda_set_variables.push(LambdaSet(Type::Variable(fresh))); } else { unreachable!("at this point there should be only vars in there"); @@ -698,8 +701,12 @@ pub fn freshen_opaque_def( .map(|_| Type::Variable(var_store.fresh())) .collect(); + // TODO this gets ignored; is that a problem + let mut introduced_variables = IntroducedVariables::default(); + instantiate_and_freshen_alias_type( var_store, + &mut introduced_variables, &opaque.type_variables, fresh_arguments, &opaque.lambda_set_variables, diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 6987ac4b4c..6ced6159a9 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -5509,4 +5509,24 @@ mod solve_expr { r#"Id [ A, B, C { a : Str }e ] -> Str"#, ) } + + #[test] + fn lambda_set_within_alias_is_quantified() { + infer_eq_without_problem( + indoc!( + r#" + app "test" provides [ effectAlways ] to "./platform" + + Effect a : [ @Effect ({} -> a) ] + + effectAlways : a -> Effect a + effectAlways = \x -> + inner = \{} -> x + + @Effect inner + "# + ), + r#"a -> Effect [ inner a ]*"#, + ) + } } From 97b0e3df9bd34799ff881913e8e055eff85c27dc Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 14:50:23 +0100 Subject: [PATCH 120/132] more efficient approach for finding new rigids --- compiler/constrain/src/expr.rs | 30 +++++++++++++++++------------- reporting/tests/helpers/mod.rs | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 848009f399..1e21eabb4c 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1642,8 +1642,6 @@ fn instantiate_rigids( let mut annotation = annotation.clone(); let mut rigid_substitution: ImMap = ImMap::default(); - let outside_rigids: Vec = ftv.values().copied().collect(); - for (name, var) in introduced_vars.var_by_name.iter() { use std::collections::hash_map::Entry::*; @@ -1655,11 +1653,27 @@ fn instantiate_rigids( Vacant(vacant) => { // It's possible to use this rigid in nested defs vacant.insert(*var); - // new_rigids.push(*var); + new_rigids.push(*var); } } } + // wildcards are always freshly introduced in this annotation + for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { + ftv.insert(format!("*{}", i).into(), *wildcard); + new_rigids.push(*wildcard); + } + + // lambda set vars are always freshly introduced in this annotation + for var in introduced_vars.lambda_sets.iter() { + new_rigids.push(*var); + } + + // lambda set vars are always freshly introduced in this annotation + for var in introduced_vars.inferred.iter() { + new_rigids.push(*var); + } + // Instantiate rigid variables if !rigid_substitution.is_empty() { annotation.substitute(&rigid_substitution); @@ -1670,20 +1684,10 @@ fn instantiate_rigids( &Loc::at(loc_pattern.region, &annotation), ) { for (symbol, loc_type) in new_headers { - for var in loc_type.value.variables() { - // a rigid is only new if this annotation is the first occurrence of this rigid - if !outside_rigids.contains(&var) { - new_rigids.push(var); - } - } headers.insert(symbol, loc_type); } } - for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { - ftv.insert(format!("*{}", i).into(), *wildcard); - } - annotation } diff --git a/reporting/tests/helpers/mod.rs b/reporting/tests/helpers/mod.rs index eb081ccfd3..a9eea79d7a 100644 --- a/reporting/tests/helpers/mod.rs +++ b/reporting/tests/helpers/mod.rs @@ -158,7 +158,7 @@ pub fn can_expr_with<'a>( let constraint = constrain_expr( &mut constraints, &roc_constrain::expr::Env { - rigids: ImMap::default(), + rigids: MutMap::default(), home, }, loc_expr.region, From 48b0bbe87437eca6f1188b3b56971e64cacf727f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 14:55:45 +0100 Subject: [PATCH 121/132] cleanup --- compiler/constrain/src/expr.rs | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 1e21eabb4c..7273f7caa3 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1659,33 +1659,26 @@ fn instantiate_rigids( } // wildcards are always freshly introduced in this annotation - for (i, wildcard) in introduced_vars.wildcards.iter().enumerate() { - ftv.insert(format!("*{}", i).into(), *wildcard); - new_rigids.push(*wildcard); - } + new_rigids.extend(introduced_vars.wildcards.iter().copied()); + + // inferred vars are always freshly introduced in this annotation + new_rigids.extend(introduced_vars.inferred.iter().copied()); // lambda set vars are always freshly introduced in this annotation - for var in introduced_vars.lambda_sets.iter() { - new_rigids.push(*var); - } - - // lambda set vars are always freshly introduced in this annotation - for var in introduced_vars.inferred.iter() { - new_rigids.push(*var); - } + new_rigids.extend(introduced_vars.lambda_sets.iter().copied()); // Instantiate rigid variables if !rigid_substitution.is_empty() { annotation.substitute(&rigid_substitution); } - if let Some(new_headers) = crate::pattern::headers_from_annotation( - &loc_pattern.value, - &Loc::at(loc_pattern.region, &annotation), - ) { - for (symbol, loc_type) in new_headers { - headers.insert(symbol, loc_type); - } + let loc_annotation_ref = Loc::at(loc_pattern.region, &annotation); + if let Pattern::Identifier(symbol) = loc_pattern.value { + headers.insert(symbol, Loc::at(loc_pattern.region, annotation.clone())); + } else if let Some(new_headers) = + crate::pattern::headers_from_annotation(&loc_pattern.value, &loc_annotation_ref) + { + headers.extend(new_headers) } annotation From 80956061dd540c57969d0e7be4b98c04abc53d6e Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 15:03:59 +0100 Subject: [PATCH 122/132] fix formatting bug in type pretty-print --- compiler/solve/tests/solve_expr.rs | 2 +- compiler/types/src/pretty_print.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 6ced6159a9..881a4556e9 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -5526,7 +5526,7 @@ mod solve_expr { @Effect inner "# ), - r#"a -> Effect [ inner a ]*"#, + r#"a -> Effect a"#, ) } } diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index bc416fe16a..6824f4abd0 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -382,7 +382,7 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa _ => write_parens!(write_parens, buf, { write_symbol(env, *symbol, buf); - for var_index in args.into_iter() { + for var_index in args.named_type_arguments() { let var = subs[var_index]; buf.push(' '); write_content( From 0464da84c0158debc27b89a52b45c6eb91bb7d76 Mon Sep 17 00:00:00 2001 From: Brian Carroll Date: Sat, 5 Mar 2022 14:21:33 +0000 Subject: [PATCH 123/132] Rename CLI --backend to --target since that's what it has always meant --- Earthfile | 2 +- cli/src/lib.rs | 87 +++++++++++++++++------------------- cli/tests/cli_run.rs | 6 +-- examples/hello-web/README.md | 2 +- 4 files changed, 47 insertions(+), 50 deletions(-) diff --git a/Earthfile b/Earthfile index 9062be8ea8..d11c9d1b6d 100644 --- a/Earthfile +++ b/Earthfile @@ -93,7 +93,7 @@ test-rust: RUN --mount=type=cache,target=$SCCACHE_DIR \ repl_test/test_wasm.sh && sccache --show-stats # run i386 (32-bit linux) cli tests - RUN echo "4" | cargo run --locked --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc + RUN echo "4" | cargo run --locked --release --features="target-x86" -- --target=x86_32 examples/benchmarks/NQueens.roc RUN --mount=type=cache,target=$SCCACHE_DIR \ cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats diff --git a/cli/src/lib.rs b/cli/src/lib.rs index b790552538..e154eeb83e 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -34,7 +34,7 @@ pub const FLAG_DEV: &str = "dev"; pub const FLAG_OPTIMIZE: &str = "optimize"; pub const FLAG_OPT_SIZE: &str = "opt-size"; pub const FLAG_LIB: &str = "lib"; -pub const FLAG_BACKEND: &str = "backend"; +pub const FLAG_TARGET: &str = "target"; pub const FLAG_TIME: &str = "time"; pub const FLAG_LINK: &str = "roc-linker"; pub const FLAG_PRECOMPILED: &str = "precompiled-host"; @@ -42,7 +42,6 @@ pub const FLAG_VALGRIND: &str = "valgrind"; pub const FLAG_CHECK: &str = "check"; pub const ROC_FILE: &str = "ROC_FILE"; pub const ROC_DIR: &str = "ROC_DIR"; -pub const BACKEND: &str = "BACKEND"; pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES"; pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP"; @@ -76,12 +75,11 @@ pub fn build_app<'a>() -> App<'a> { .required(false), ) .arg( - Arg::new(FLAG_BACKEND) - .long(FLAG_BACKEND) - .about("Choose a different backend") - // .requires(BACKEND) - .default_value(Backend::default().as_str()) - .possible_values(Backend::OPTIONS) + Arg::new(FLAG_TARGET) + .long(FLAG_TARGET) + .about("Choose a different target") + .default_value(Target::default().as_str()) + .possible_values(Target::OPTIONS) .required(false), ) .arg( @@ -212,12 +210,11 @@ pub fn build_app<'a>() -> App<'a> { .required(false), ) .arg( - Arg::new(FLAG_BACKEND) - .long(FLAG_BACKEND) - .about("Choose a different backend") - // .requires(BACKEND) - .default_value(Backend::default().as_str()) - .possible_values(Backend::OPTIONS) + Arg::new(FLAG_TARGET) + .long(FLAG_TARGET) + .about("Choose a different target") + .default_value(Target::default().as_str()) + .possible_values(Target::OPTIONS) .required(false), ) .arg( @@ -273,12 +270,12 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result { use std::str::FromStr; use BuildConfig::*; - let backend = match matches.value_of(FLAG_BACKEND) { - Some(name) => Backend::from_str(name).unwrap(), - None => Backend::default(), + let target = match matches.value_of(FLAG_TARGET) { + Some(name) => Target::from_str(name).unwrap(), + None => Target::default(), }; - let target = backend.to_triple(); + let triple = target.to_triple(); let arena = Bump::new(); let filename = matches.value_of(ROC_FILE).unwrap(); @@ -306,10 +303,10 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result { let surgically_link = matches.is_present(FLAG_LINK); let precompiled = matches.is_present(FLAG_PRECOMPILED); - if surgically_link && !roc_linker::supported(&link_type, &target) { + if surgically_link && !roc_linker::supported(&link_type, &triple) { panic!( "Link type, {:?}, with target, {}, not supported by roc linker", - link_type, target + link_type, triple ); } @@ -338,7 +335,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result { let target_valgrind = matches.is_present(FLAG_VALGRIND); let res_binary_path = build_file( &arena, - &target, + &triple, src_dir, path, opt_level, @@ -377,7 +374,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result { Ok(outcome.status_code()) } BuildAndRun { roc_file_arg_index } => { - let mut cmd = match target.architecture { + let mut cmd = match triple.architecture { Architecture::Wasm32 => { // If possible, report the generated executable name relative to the current dir. let generated_filename = binary_path @@ -398,7 +395,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result { _ => Command::new(&binary_path), }; - if let Architecture::Wasm32 = target.architecture { + if let Architecture::Wasm32 = triple.architecture { cmd.arg(binary_path); } @@ -503,43 +500,43 @@ fn run_with_wasmer(_wasm_path: &std::path::Path, _args: &[String]) { println!("Running wasm files not support"); } -enum Backend { +enum Target { Host, X86_32, X86_64, Wasm32, } -impl Default for Backend { +impl Default for Target { fn default() -> Self { - Backend::Host + Target::Host } } -impl Backend { +impl Target { const fn as_str(&self) -> &'static str { match self { - Backend::Host => "host", - Backend::X86_32 => "x86_32", - Backend::X86_64 => "x86_64", - Backend::Wasm32 => "wasm32", + Target::Host => "host", + Target::X86_32 => "x86_32", + Target::X86_64 => "x86_64", + Target::Wasm32 => "wasm32", } } /// NOTE keep up to date! const OPTIONS: &'static [&'static str] = &[ - Backend::Host.as_str(), - Backend::X86_32.as_str(), - Backend::X86_64.as_str(), - Backend::Wasm32.as_str(), + Target::Host.as_str(), + Target::X86_32.as_str(), + Target::X86_64.as_str(), + Target::Wasm32.as_str(), ]; fn to_triple(&self) -> Triple { let mut triple = Triple::unknown(); match self { - Backend::Host => Triple::host(), - Backend::X86_32 => { + Target::Host => Triple::host(), + Target::X86_32 => { triple.architecture = Architecture::X86_32(X86_32Architecture::I386); triple.binary_format = BinaryFormat::Elf; @@ -548,13 +545,13 @@ impl Backend { triple } - Backend::X86_64 => { + Target::X86_64 => { triple.architecture = Architecture::X86_64; triple.binary_format = BinaryFormat::Elf; triple } - Backend::Wasm32 => { + Target::Wasm32 => { triple.architecture = Architecture::Wasm32; triple.binary_format = BinaryFormat::Wasm; @@ -564,21 +561,21 @@ impl Backend { } } -impl std::fmt::Display for Backend { +impl std::fmt::Display for Target { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.as_str()) } } -impl std::str::FromStr for Backend { +impl std::str::FromStr for Target { type Err = (); fn from_str(s: &str) -> Result { match s { - "host" => Ok(Backend::Host), - "x86_32" => Ok(Backend::X86_32), - "x86_64" => Ok(Backend::X86_64), - "wasm32" => Ok(Backend::Wasm32), + "host" => Ok(Target::Host), + "x86_32" => Ok(Target::X86_32), + "x86_64" => Ok(Target::X86_64), + "wasm32" => Ok(Target::Wasm32), _ => Err(()), } } diff --git a/cli/tests/cli_run.rs b/cli/tests/cli_run.rs index 9e7aee0d8b..164bdd86ce 100644 --- a/cli/tests/cli_run.rs +++ b/cli/tests/cli_run.rs @@ -194,7 +194,7 @@ mod cli_run { ) { assert_eq!(input_file, None, "Wasm does not support input files"); let mut flags = flags.to_vec(); - flags.push("--backend=wasm32"); + flags.push("--target=wasm32"); let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat()); if !compile_out.stderr.is_empty() { @@ -565,7 +565,7 @@ mod cli_run { &file_name, benchmark.stdin, benchmark.executable_filename, - &["--backend=x86_32"], + &["--target=x86_32"], benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.expected_ending, benchmark.use_valgrind, @@ -575,7 +575,7 @@ mod cli_run { &file_name, benchmark.stdin, benchmark.executable_filename, - &["--backend=x86_32", "--optimize"], + &["--target=x86_32", "--optimize"], benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))), benchmark.expected_ending, benchmark.use_valgrind, diff --git a/examples/hello-web/README.md b/examples/hello-web/README.md index f0beccdb6b..4401c8d367 100644 --- a/examples/hello-web/README.md +++ b/examples/hello-web/README.md @@ -3,7 +3,7 @@ To run, go to the project home directory and run: ```bash -$ cargo run -- build --backend=wasm32 examples/hello-web/Hello.roc +$ cargo run -- build --target=wasm32 examples/hello-web/Hello.roc ``` Then `cd` into the example directory and run any web server that can handle WebAssembly. From 5e48577d29eb1c1a8cb60c83ad1676bbfcad3863 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 15:32:55 +0100 Subject: [PATCH 124/132] remove clones in most cases in can/def --- compiler/can/src/def.rs | 153 +++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 58 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 6411441d98..f6372dc782 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -864,6 +864,32 @@ fn pattern_to_vars_by_symbol( } } +fn single_typed_can_def( + loc_can_pattern: Loc, + loc_can_expr: Loc, + expr_var: Variable, + loc_annotation: Loc, + pattern_vars: SendMap, +) -> Def { + let def_annotation = Annotation { + signature: loc_annotation.value.typ, + introduced_variables: loc_annotation.value.introduced_variables, + aliases: loc_annotation.value.aliases, + region: loc_annotation.region, + }; + + Def { + expr_var, + loc_pattern: loc_can_pattern, + loc_expr: Loc { + region: loc_can_expr.region, + value: loc_can_expr.value, + }, + pattern_vars, + annotation: Some(def_annotation), + } +} + // TODO trim down these arguments! #[allow(clippy::too_many_arguments)] #[allow(clippy::cognitive_complexity)] @@ -1003,13 +1029,11 @@ fn canonicalize_pending_def<'a>( canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store); // Record all the annotation's references in output.references.lookups - for symbol in ann.references { - output.references.lookups.insert(symbol); - output.references.referenced_type_defs.insert(symbol); + for symbol in ann.references.iter() { + output.references.lookups.insert(*symbol); + output.references.referenced_type_defs.insert(*symbol); } - let typ = ann.typ; - for (symbol, alias) in ann.aliases.clone() { aliases.insert(symbol, alias); } @@ -1041,9 +1065,6 @@ fn canonicalize_pending_def<'a>( // reset the tailcallable_symbol env.tailcallable_symbol = outer_identifier; - // see below: a closure needs a fresh References! - let mut is_closure = false; - // First, make sure we are actually assigning an identifier instead of (for example) a tag. // // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, @@ -1051,7 +1072,6 @@ fn canonicalize_pending_def<'a>( // // Only defs of the form (foo = ...) can be closure declarations or self tail calls. if let ( - &ast::Pattern::Identifier(_name), &Pattern::Identifier(ref defined_symbol), &Closure(ClosureData { function_type, @@ -1064,13 +1084,8 @@ fn canonicalize_pending_def<'a>( ref captured_symbols, .. }), - ) = ( - &loc_pattern.value, - &loc_can_pattern.value, - &loc_can_expr.value, - ) { - is_closure = true; - + ) = (&loc_can_pattern.value, &loc_can_expr.value) + { // Since everywhere in the code it'll be referred to by its defined name, // remove its generated name from the closure map. (We'll re-insert it later.) let references = env.closures.remove(symbol).unwrap_or_else(|| { @@ -1113,52 +1128,74 @@ fn canonicalize_pending_def<'a>( loc_body: body.clone(), }); - // TODO exploit this fact to remove clones below - debug_assert_eq!( - vec![*defined_symbol], - scope.idents().map(|t| t.1 .0).filter(|x| vars_by_symbol.contains_key(x)).collect::>() + // Functions' references don't count in defs. + // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its + // parent commit for the bug this fixed! + let refs = References::new(); + + refs_by_symbol.insert(*defined_symbol, (loc_can_pattern.region, refs)); + + let symbol = *defined_symbol; + let def = single_typed_can_def( + loc_can_pattern, + loc_can_expr, + expr_var, + Loc::at(loc_ann.region, ann), + vars_by_symbol.clone(), ); - } + can_defs_by_symbol.insert(symbol, def); + } else { + // Store the referenced locals in the refs_by_symbol map, so we can later figure out + // which defined names reference each other. - // Store the referenced locals in the refs_by_symbol map, so we can later figure out - // which defined names reference each other. - for (_, (symbol, region)) in scope.idents() { - if !vars_by_symbol.contains_key(symbol) { - continue; - } + if let Loc { + region, + value: Pattern::Identifier(symbol), + } = loc_can_pattern + { + let refs = can_output.references; + refs_by_symbol.insert(symbol, (region, refs)); - let refs = - // Functions' references don't count in defs. - // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its - // parent commit for the bug this fixed! - if is_closure { - References::new() - } else { - can_output.references.clone() - }; - - refs_by_symbol.insert(*symbol, (*region, refs)); - - can_defs_by_symbol.insert( - *symbol, - Def { + let def = single_typed_can_def( + loc_can_pattern, + loc_can_expr, expr_var, - // TODO try to remove this .clone()! - loc_pattern: loc_can_pattern.clone(), - loc_expr: Loc { - region: loc_can_expr.region, - // TODO try to remove this .clone()! - value: loc_can_expr.value.clone(), - }, - pattern_vars: vars_by_symbol.clone(), - annotation: Some(Annotation { - signature: typ.clone(), - introduced_variables: ann.introduced_variables.clone(), - aliases: ann.aliases.clone(), - region: loc_ann.region, - }), - }, - ); + Loc::at(loc_ann.region, ann), + vars_by_symbol.clone(), + ); + can_defs_by_symbol.insert(symbol, def); + } else { + for (_, (symbol, region)) in scope.idents() { + if !vars_by_symbol.contains_key(symbol) { + continue; + } + + let refs = can_output.references.clone(); + + refs_by_symbol.insert(*symbol, (*region, refs)); + + can_defs_by_symbol.insert( + *symbol, + Def { + expr_var, + // TODO try to remove this .clone()! + loc_pattern: loc_can_pattern.clone(), + loc_expr: Loc { + region: loc_can_expr.region, + // TODO try to remove this .clone()! + value: loc_can_expr.value.clone(), + }, + pattern_vars: vars_by_symbol.clone(), + annotation: Some(Annotation { + signature: ann.typ.clone(), + introduced_variables: ann.introduced_variables.clone(), + aliases: ann.aliases.clone(), + region: loc_ann.region, + }), + }, + ); + } + } } } // If we have a pattern, then the def has a body (that is, it's not a From b421df2a2834996b8f0587f10657e2002331f81d Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 15:50:00 +0100 Subject: [PATCH 125/132] transform TypedDef to use fewer clones --- compiler/can/src/def.rs | 198 ++++++++++++++++++---------------------- 1 file changed, 91 insertions(+), 107 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index f6372dc782..5687c03fc2 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1024,21 +1024,23 @@ fn canonicalize_pending_def<'a>( InvalidAlias { .. } => { // invalid aliases and opaques (shadowed, incorrect patterns) get ignored } - TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => { - let ann = + TypedBody(_loc_pattern, loc_can_pattern, loc_ann, loc_expr) => { + let type_annotation = canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store); // Record all the annotation's references in output.references.lookups - for symbol in ann.references.iter() { + for symbol in type_annotation.references.iter() { output.references.lookups.insert(*symbol); output.references.referenced_type_defs.insert(*symbol); } - for (symbol, alias) in ann.aliases.clone() { + for (symbol, alias) in type_annotation.aliases.clone() { aliases.insert(symbol, alias); } - output.introduced_variables.union(&ann.introduced_variables); + output + .introduced_variables + .union(&type_annotation.introduced_variables); // bookkeeping for tail-call detection. If we're assigning to an // identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called. @@ -1071,130 +1073,112 @@ fn canonicalize_pending_def<'a>( // which also implies it's not a self tail call! // // Only defs of the form (foo = ...) can be closure declarations or self tail calls. - if let ( - &Pattern::Identifier(ref defined_symbol), - &Closure(ClosureData { + if let Loc { + region, + value: Pattern::Identifier(symbol), + } = loc_can_pattern + { + if let &Closure(ClosureData { function_type, closure_type, closure_ext_var, return_type, - name: ref symbol, + name: ref closure_name, ref arguments, loc_body: ref body, ref captured_symbols, .. - }), - ) = (&loc_can_pattern.value, &loc_can_expr.value) - { - // Since everywhere in the code it'll be referred to by its defined name, - // remove its generated name from the closure map. (We'll re-insert it later.) - let references = env.closures.remove(symbol).unwrap_or_else(|| { - panic!( - "Tried to remove symbol {:?} from procedures, but it was not found: {:?}", - symbol, env.closures - ) - }); - - // Re-insert the closure into the map, under its defined name. - // closures don't have a name, and therefore pick a fresh symbol. But in this - // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` - // and we want to reference it by that name. - env.closures.insert(*defined_symbol, references); - - // The closure is self tail recursive iff it tail calls itself (by defined name). - let is_recursive = match can_output.tail_call { - Some(ref symbol) if symbol == defined_symbol => Recursive::TailRecursive, - _ => Recursive::NotRecursive, - }; - - // Recursion doesn't count as referencing. (If it did, all recursive functions - // would result in circular def errors!) - refs_by_symbol - .entry(*defined_symbol) - .and_modify(|(_, refs)| { - refs.lookups = refs.lookups.without(defined_symbol); + }) = &loc_can_expr.value + { + // Since everywhere in the code it'll be referred to by its defined name, + // remove its generated name from the closure map. (We'll re-insert it later.) + let references = env.closures.remove(closure_name).unwrap_or_else(|| { + panic!( + "Tried to remove symbol {:?} from procedures, but it was not found: {:?}", + closure_name, env.closures + ) }); - // renamed_closure_def = Some(&defined_symbol); - loc_can_expr.value = Closure(ClosureData { - function_type, - closure_type, - closure_ext_var, - return_type, - name: *defined_symbol, - captured_symbols: captured_symbols.clone(), - recursive: is_recursive, - arguments: arguments.clone(), - loc_body: body.clone(), - }); + // Re-insert the closure into the map, under its defined name. + // closures don't have a name, and therefore pick a fresh symbol. But in this + // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` + // and we want to reference it by that name. + env.closures.insert(symbol, references); - // Functions' references don't count in defs. - // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its - // parent commit for the bug this fixed! - let refs = References::new(); + // The closure is self tail recursive iff it tail calls itself (by defined name). + let is_recursive = match can_output.tail_call { + Some(tail_symbol) if tail_symbol == symbol => Recursive::TailRecursive, + _ => Recursive::NotRecursive, + }; - refs_by_symbol.insert(*defined_symbol, (loc_can_pattern.region, refs)); + // Recursion doesn't count as referencing. (If it did, all recursive functions + // would result in circular def errors!) + refs_by_symbol.entry(symbol).and_modify(|(_, refs)| { + refs.lookups = refs.lookups.without(&symbol); + }); + + // renamed_closure_def = Some(&symbol); + loc_can_expr.value = Closure(ClosureData { + function_type, + closure_type, + closure_ext_var, + return_type, + name: symbol, + captured_symbols: captured_symbols.clone(), + recursive: is_recursive, + arguments: arguments.clone(), + loc_body: body.clone(), + }); + + // Functions' references don't count in defs. + // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its + // parent commit for the bug this fixed! + let refs = References::new(); + + refs_by_symbol.insert(symbol, (loc_can_pattern.region, refs)); + } else { + let refs = can_output.references; + refs_by_symbol.insert(symbol, (region, refs)); + } - let symbol = *defined_symbol; let def = single_typed_can_def( loc_can_pattern, loc_can_expr, expr_var, - Loc::at(loc_ann.region, ann), + Loc::at(loc_ann.region, type_annotation), vars_by_symbol.clone(), ); can_defs_by_symbol.insert(symbol, def); } else { - // Store the referenced locals in the refs_by_symbol map, so we can later figure out - // which defined names reference each other. - - if let Loc { - region, - value: Pattern::Identifier(symbol), - } = loc_can_pattern - { - let refs = can_output.references; - refs_by_symbol.insert(symbol, (region, refs)); - - let def = single_typed_can_def( - loc_can_pattern, - loc_can_expr, - expr_var, - Loc::at(loc_ann.region, ann), - vars_by_symbol.clone(), - ); - can_defs_by_symbol.insert(symbol, def); - } else { - for (_, (symbol, region)) in scope.idents() { - if !vars_by_symbol.contains_key(symbol) { - continue; - } - - let refs = can_output.references.clone(); - - refs_by_symbol.insert(*symbol, (*region, refs)); - - can_defs_by_symbol.insert( - *symbol, - Def { - expr_var, - // TODO try to remove this .clone()! - loc_pattern: loc_can_pattern.clone(), - loc_expr: Loc { - region: loc_can_expr.region, - // TODO try to remove this .clone()! - value: loc_can_expr.value.clone(), - }, - pattern_vars: vars_by_symbol.clone(), - annotation: Some(Annotation { - signature: ann.typ.clone(), - introduced_variables: ann.introduced_variables.clone(), - aliases: ann.aliases.clone(), - region: loc_ann.region, - }), - }, - ); + for (_, (symbol, region)) in scope.idents() { + if !vars_by_symbol.contains_key(symbol) { + continue; } + + let refs = can_output.references.clone(); + + refs_by_symbol.insert(*symbol, (*region, refs)); + + can_defs_by_symbol.insert( + *symbol, + Def { + expr_var, + // TODO try to remove this .clone()! + loc_pattern: loc_can_pattern.clone(), + loc_expr: Loc { + region: loc_can_expr.region, + // TODO try to remove this .clone()! + value: loc_can_expr.value.clone(), + }, + pattern_vars: vars_by_symbol.clone(), + annotation: Some(Annotation { + signature: type_annotation.typ.clone(), + introduced_variables: type_annotation.introduced_variables.clone(), + aliases: type_annotation.aliases.clone(), + region: loc_ann.region, + }), + }, + ); } } } From 06ff93cace37d445594e64b7372354871cd51d01 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 15:50:41 +0100 Subject: [PATCH 126/132] Revert "remove clones in most cases in can/def" This reverts commit 5e48577d29eb1c1a8cb60c83ad1676bbfcad3863. Will put it into a separate PR --- compiler/can/src/def.rs | 155 +++++++++++++++------------------------- 1 file changed, 59 insertions(+), 96 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index f6372dc782..6411441d98 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -864,32 +864,6 @@ fn pattern_to_vars_by_symbol( } } -fn single_typed_can_def( - loc_can_pattern: Loc, - loc_can_expr: Loc, - expr_var: Variable, - loc_annotation: Loc, - pattern_vars: SendMap, -) -> Def { - let def_annotation = Annotation { - signature: loc_annotation.value.typ, - introduced_variables: loc_annotation.value.introduced_variables, - aliases: loc_annotation.value.aliases, - region: loc_annotation.region, - }; - - Def { - expr_var, - loc_pattern: loc_can_pattern, - loc_expr: Loc { - region: loc_can_expr.region, - value: loc_can_expr.value, - }, - pattern_vars, - annotation: Some(def_annotation), - } -} - // TODO trim down these arguments! #[allow(clippy::too_many_arguments)] #[allow(clippy::cognitive_complexity)] @@ -1029,11 +1003,13 @@ fn canonicalize_pending_def<'a>( canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store); // Record all the annotation's references in output.references.lookups - for symbol in ann.references.iter() { - output.references.lookups.insert(*symbol); - output.references.referenced_type_defs.insert(*symbol); + for symbol in ann.references { + output.references.lookups.insert(symbol); + output.references.referenced_type_defs.insert(symbol); } + let typ = ann.typ; + for (symbol, alias) in ann.aliases.clone() { aliases.insert(symbol, alias); } @@ -1065,6 +1041,9 @@ fn canonicalize_pending_def<'a>( // reset the tailcallable_symbol env.tailcallable_symbol = outer_identifier; + // see below: a closure needs a fresh References! + let mut is_closure = false; + // First, make sure we are actually assigning an identifier instead of (for example) a tag. // // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, @@ -1072,6 +1051,7 @@ fn canonicalize_pending_def<'a>( // // Only defs of the form (foo = ...) can be closure declarations or self tail calls. if let ( + &ast::Pattern::Identifier(_name), &Pattern::Identifier(ref defined_symbol), &Closure(ClosureData { function_type, @@ -1084,8 +1064,13 @@ fn canonicalize_pending_def<'a>( ref captured_symbols, .. }), - ) = (&loc_can_pattern.value, &loc_can_expr.value) - { + ) = ( + &loc_pattern.value, + &loc_can_pattern.value, + &loc_can_expr.value, + ) { + is_closure = true; + // Since everywhere in the code it'll be referred to by its defined name, // remove its generated name from the closure map. (We'll re-insert it later.) let references = env.closures.remove(symbol).unwrap_or_else(|| { @@ -1128,74 +1113,52 @@ fn canonicalize_pending_def<'a>( loc_body: body.clone(), }); - // Functions' references don't count in defs. - // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its - // parent commit for the bug this fixed! - let refs = References::new(); - - refs_by_symbol.insert(*defined_symbol, (loc_can_pattern.region, refs)); - - let symbol = *defined_symbol; - let def = single_typed_can_def( - loc_can_pattern, - loc_can_expr, - expr_var, - Loc::at(loc_ann.region, ann), - vars_by_symbol.clone(), + // TODO exploit this fact to remove clones below + debug_assert_eq!( + vec![*defined_symbol], + scope.idents().map(|t| t.1 .0).filter(|x| vars_by_symbol.contains_key(x)).collect::>() ); - can_defs_by_symbol.insert(symbol, def); - } else { - // Store the referenced locals in the refs_by_symbol map, so we can later figure out - // which defined names reference each other. + } - if let Loc { - region, - value: Pattern::Identifier(symbol), - } = loc_can_pattern - { - let refs = can_output.references; - refs_by_symbol.insert(symbol, (region, refs)); - - let def = single_typed_can_def( - loc_can_pattern, - loc_can_expr, - expr_var, - Loc::at(loc_ann.region, ann), - vars_by_symbol.clone(), - ); - can_defs_by_symbol.insert(symbol, def); - } else { - for (_, (symbol, region)) in scope.idents() { - if !vars_by_symbol.contains_key(symbol) { - continue; - } - - let refs = can_output.references.clone(); - - refs_by_symbol.insert(*symbol, (*region, refs)); - - can_defs_by_symbol.insert( - *symbol, - Def { - expr_var, - // TODO try to remove this .clone()! - loc_pattern: loc_can_pattern.clone(), - loc_expr: Loc { - region: loc_can_expr.region, - // TODO try to remove this .clone()! - value: loc_can_expr.value.clone(), - }, - pattern_vars: vars_by_symbol.clone(), - annotation: Some(Annotation { - signature: ann.typ.clone(), - introduced_variables: ann.introduced_variables.clone(), - aliases: ann.aliases.clone(), - region: loc_ann.region, - }), - }, - ); - } + // Store the referenced locals in the refs_by_symbol map, so we can later figure out + // which defined names reference each other. + for (_, (symbol, region)) in scope.idents() { + if !vars_by_symbol.contains_key(symbol) { + continue; } + + let refs = + // Functions' references don't count in defs. + // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its + // parent commit for the bug this fixed! + if is_closure { + References::new() + } else { + can_output.references.clone() + }; + + refs_by_symbol.insert(*symbol, (*region, refs)); + + can_defs_by_symbol.insert( + *symbol, + Def { + expr_var, + // TODO try to remove this .clone()! + loc_pattern: loc_can_pattern.clone(), + loc_expr: Loc { + region: loc_can_expr.region, + // TODO try to remove this .clone()! + value: loc_can_expr.value.clone(), + }, + pattern_vars: vars_by_symbol.clone(), + annotation: Some(Annotation { + signature: typ.clone(), + introduced_variables: ann.introduced_variables.clone(), + aliases: ann.aliases.clone(), + region: loc_ann.region, + }), + }, + ); } } // If we have a pattern, then the def has a body (that is, it's not a From 87e1b5bba62fa2aed7159f126c36523eeb9fdec9 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 15:53:24 +0100 Subject: [PATCH 127/132] formatting --- compiler/can/src/def.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 6411441d98..ce0d157735 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1116,7 +1116,11 @@ fn canonicalize_pending_def<'a>( // TODO exploit this fact to remove clones below debug_assert_eq!( vec![*defined_symbol], - scope.idents().map(|t| t.1 .0).filter(|x| vars_by_symbol.contains_key(x)).collect::>() + scope + .idents() + .map(|t| t.1 .0) + .filter(|x| vars_by_symbol.contains_key(x)) + .collect::>() ); } From cd203d20a376f15b7a99951f0310e3cb06c99871 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 16:12:19 +0100 Subject: [PATCH 128/132] also remove clones for Body --- compiler/can/src/def.rs | 175 ++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 87 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 5687c03fc2..6bca698395 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -864,19 +864,19 @@ fn pattern_to_vars_by_symbol( } } -fn single_typed_can_def( +fn single_can_def( loc_can_pattern: Loc, loc_can_expr: Loc, expr_var: Variable, - loc_annotation: Loc, + opt_loc_annotation: Option>, pattern_vars: SendMap, ) -> Def { - let def_annotation = Annotation { + let def_annotation = opt_loc_annotation.map(|loc_annotation| Annotation { signature: loc_annotation.value.typ, introduced_variables: loc_annotation.value.introduced_variables, aliases: loc_annotation.value.aliases, region: loc_annotation.region, - }; + }); Def { expr_var, @@ -886,7 +886,7 @@ fn single_typed_can_def( value: loc_can_expr.value, }, pattern_vars, - annotation: Some(def_annotation), + annotation: def_annotation, } } @@ -1141,11 +1141,11 @@ fn canonicalize_pending_def<'a>( refs_by_symbol.insert(symbol, (region, refs)); } - let def = single_typed_can_def( + let def = single_can_def( loc_can_pattern, loc_can_expr, expr_var, - Loc::at(loc_ann.region, type_annotation), + Some(Loc::at(loc_ann.region, type_annotation)), vars_by_symbol.clone(), ); can_defs_by_symbol.insert(symbol, def); @@ -1211,108 +1211,109 @@ fn canonicalize_pending_def<'a>( // reset the tailcallable_symbol env.tailcallable_symbol = outer_identifier; - // see below: a closure needs a fresh References! - let mut is_closure = false; - // First, make sure we are actually assigning an identifier instead of (for example) a tag. // // If we're assigning (UserId userId) = ... then this is certainly not a closure declaration, // which also implies it's not a self tail call! // // Only defs of the form (foo = ...) can be closure declarations or self tail calls. - if let ( - &ast::Pattern::Identifier(_name), - &Pattern::Identifier(ref defined_symbol), - &Closure(ClosureData { + if let Loc { + region, + value: Pattern::Identifier(symbol), + } = loc_can_pattern + { + if let &Closure(ClosureData { function_type, closure_type, closure_ext_var, return_type, - name: ref symbol, + name: ref closure_name, ref arguments, loc_body: ref body, ref captured_symbols, .. - }), - ) = ( - &loc_pattern.value, - &loc_can_pattern.value, - &loc_can_expr.value, - ) { - is_closure = true; - - // Since everywhere in the code it'll be referred to by its defined name, - // remove its generated name from the closure map. (We'll re-insert it later.) - let references = env.closures.remove(symbol).unwrap_or_else(|| { - panic!( - "Tried to remove symbol {:?} from procedures, but it was not found: {:?}", - symbol, env.closures - ) - }); - - // Re-insert the closure into the map, under its defined name. - // closures don't have a name, and therefore pick a fresh symbol. But in this - // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` - // and we want to reference it by that name. - env.closures.insert(*defined_symbol, references); - - // The closure is self tail recursive iff it tail calls itself (by defined name). - let is_recursive = match can_output.tail_call { - Some(ref symbol) if symbol == defined_symbol => Recursive::TailRecursive, - _ => Recursive::NotRecursive, - }; - - // Recursion doesn't count as referencing. (If it did, all recursive functions - // would result in circular def errors!) - refs_by_symbol - .entry(*defined_symbol) - .and_modify(|(_, refs)| { - refs.lookups = refs.lookups.without(defined_symbol); + }) = &loc_can_expr.value + { + // Since everywhere in the code it'll be referred to by its defined name, + // remove its generated name from the closure map. (We'll re-insert it later.) + let references = env.closures.remove(closure_name).unwrap_or_else(|| { + panic!( + "Tried to remove symbol {:?} from procedures, but it was not found: {:?}", + closure_name, env.closures + ) }); - loc_can_expr.value = Closure(ClosureData { - function_type, - closure_type, - closure_ext_var, - return_type, - name: *defined_symbol, - captured_symbols: captured_symbols.clone(), - recursive: is_recursive, - arguments: arguments.clone(), - loc_body: body.clone(), - }); - } + // Re-insert the closure into the map, under its defined name. + // closures don't have a name, and therefore pick a fresh symbol. But in this + // case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...` + // and we want to reference it by that name. + env.closures.insert(symbol, references); + + // The closure is self tail recursive iff it tail calls itself (by defined name). + let is_recursive = match can_output.tail_call { + Some(tail_symbol) if tail_symbol == symbol => Recursive::TailRecursive, + _ => Recursive::NotRecursive, + }; + + // Recursion doesn't count as referencing. (If it did, all recursive functions + // would result in circular def errors!) + refs_by_symbol.entry(symbol).and_modify(|(_, refs)| { + refs.lookups = refs.lookups.without(&symbol); + }); + + loc_can_expr.value = Closure(ClosureData { + function_type, + closure_type, + closure_ext_var, + return_type, + name: symbol, + captured_symbols: captured_symbols.clone(), + recursive: is_recursive, + arguments: arguments.clone(), + loc_body: body.clone(), + }); - // Store the referenced locals in the refs_by_symbol map, so we can later figure out - // which defined names reference each other. - for (symbol, region) in bindings_from_patterns(std::iter::once(&loc_can_pattern)) { - let refs = // Functions' references don't count in defs. // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its // parent commit for the bug this fixed! - if is_closure { - References::new() - } else { - can_output.references.clone() - }; + let refs = References::new(); + refs_by_symbol.insert(symbol, (region, refs)); + } else { + let refs = can_output.references.clone(); + refs_by_symbol.insert(symbol, (region, refs)); + } - refs_by_symbol.insert(symbol, (region, refs)); - - can_defs_by_symbol.insert( - symbol, - Def { - expr_var, - // TODO try to remove this .clone()! - loc_pattern: loc_can_pattern.clone(), - loc_expr: Loc { - // TODO try to remove this .clone()! - region: loc_can_expr.region, - value: loc_can_expr.value.clone(), - }, - pattern_vars: vars_by_symbol.clone(), - annotation: None, - }, + let def = single_can_def( + loc_can_pattern, + loc_can_expr, + expr_var, + None, + vars_by_symbol.clone(), ); + can_defs_by_symbol.insert(symbol, def); + } else { + // Store the referenced locals in the refs_by_symbol map, so we can later figure out + // which defined names reference each other. + for (symbol, region) in bindings_from_patterns(std::iter::once(&loc_can_pattern)) { + let refs = can_output.references.clone(); + refs_by_symbol.insert(symbol, (region, refs)); + + can_defs_by_symbol.insert( + symbol, + Def { + expr_var, + // TODO try to remove this .clone()! + loc_pattern: loc_can_pattern.clone(), + loc_expr: Loc { + // TODO try to remove this .clone()! + region: loc_can_expr.region, + value: loc_can_expr.value.clone(), + }, + pattern_vars: vars_by_symbol.clone(), + annotation: None, + }, + ); + } } output.union(can_output); From 0285a1b0ea7e8dbf19058368dd4388ad008d6938 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 16:41:29 +0100 Subject: [PATCH 129/132] cleanup --- compiler/can/src/def.rs | 99 +++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 6bca698395..1b5108dfe9 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -913,25 +913,25 @@ fn canonicalize_pending_def<'a>( AnnotationOnly(_, loc_can_pattern, loc_ann) => { // annotation sans body cannot introduce new rigids that are visible in other annotations // but the rigids can show up in type error messages, so still register them - let ann = + let type_annotation = canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store); // Record all the annotation's references in output.references.lookups - for symbol in ann.references { - output.references.lookups.insert(symbol); - output.references.referenced_type_defs.insert(symbol); + for symbol in type_annotation.references.iter() { + output.references.lookups.insert(*symbol); + output.references.referenced_type_defs.insert(*symbol); } - aliases.extend(ann.aliases.clone()); + aliases.extend(type_annotation.aliases.clone()); - output.introduced_variables.union(&ann.introduced_variables); + output + .introduced_variables + .union(&type_annotation.introduced_variables); pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var); - let typ = ann.typ; - - let arity = typ.arity(); + let arity = type_annotation.typ.arity(); let problem = match &loc_can_pattern.value { Pattern::Identifier(symbol) => RuntimeError::NoImplementationNamed { @@ -989,33 +989,44 @@ fn canonicalize_pending_def<'a>( } }; - for (_, (symbol, _)) in scope.idents() { - if !vars_by_symbol.contains_key(symbol) { - continue; - } - - // We could potentially avoid some clones here by using Rc strategically, - // but the total amount of cloning going on here should typically be minimal. - can_defs_by_symbol.insert( - *symbol, - Def { - expr_var, - // TODO try to remove this .clone()! - loc_pattern: loc_can_pattern.clone(), - loc_expr: Loc { - region: loc_can_expr.region, - // TODO try to remove this .clone()! - value: loc_can_expr.value.clone(), - }, - pattern_vars: vars_by_symbol.clone(), - annotation: Some(Annotation { - signature: typ.clone(), - introduced_variables: output.introduced_variables.clone(), - aliases: ann.aliases.clone(), - region: loc_ann.region, - }), - }, + if let Pattern::Identifier(symbol) = loc_can_pattern.value { + let def = single_can_def( + loc_can_pattern, + loc_can_expr, + expr_var, + Some(Loc::at(loc_ann.region, type_annotation)), + vars_by_symbol.clone(), ); + can_defs_by_symbol.insert(symbol, def); + } else { + for (_, (symbol, _)) in scope.idents() { + if !vars_by_symbol.contains_key(symbol) { + continue; + } + + // We could potentially avoid some clones here by using Rc strategically, + // but the total amount of cloning going on here should typically be minimal. + can_defs_by_symbol.insert( + *symbol, + Def { + expr_var, + // TODO try to remove this .clone()! + loc_pattern: loc_can_pattern.clone(), + loc_expr: Loc { + region: loc_can_expr.region, + // TODO try to remove this .clone()! + value: loc_can_expr.value.clone(), + }, + pattern_vars: vars_by_symbol.clone(), + annotation: Some(Annotation { + signature: type_annotation.typ.clone(), + introduced_variables: output.introduced_variables.clone(), + aliases: type_annotation.aliases.clone(), + region: loc_ann.region, + }), + }, + ); + } } } @@ -1073,11 +1084,7 @@ fn canonicalize_pending_def<'a>( // which also implies it's not a self tail call! // // Only defs of the form (foo = ...) can be closure declarations or self tail calls. - if let Loc { - region, - value: Pattern::Identifier(symbol), - } = loc_can_pattern - { + if let Pattern::Identifier(symbol) = loc_can_pattern.value { if let &Closure(ClosureData { function_type, closure_type, @@ -1138,7 +1145,7 @@ fn canonicalize_pending_def<'a>( refs_by_symbol.insert(symbol, (loc_can_pattern.region, refs)); } else { let refs = can_output.references; - refs_by_symbol.insert(symbol, (region, refs)); + refs_by_symbol.insert(symbol, (loc_ann.region, refs)); } let def = single_can_def( @@ -1217,11 +1224,7 @@ fn canonicalize_pending_def<'a>( // which also implies it's not a self tail call! // // Only defs of the form (foo = ...) can be closure declarations or self tail calls. - if let Loc { - region, - value: Pattern::Identifier(symbol), - } = loc_can_pattern - { + if let Pattern::Identifier(symbol) = loc_can_pattern.value { if let &Closure(ClosureData { function_type, closure_type, @@ -1277,10 +1280,10 @@ fn canonicalize_pending_def<'a>( // See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its // parent commit for the bug this fixed! let refs = References::new(); - refs_by_symbol.insert(symbol, (region, refs)); + refs_by_symbol.insert(symbol, (loc_pattern.region, refs)); } else { let refs = can_output.references.clone(); - refs_by_symbol.insert(symbol, (region, refs)); + refs_by_symbol.insert(symbol, (loc_pattern.region, refs)); } let def = single_can_def( From f2fa625886c9d54925bb655a6ef2121779574a0f Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 16:48:33 +0100 Subject: [PATCH 130/132] add test --- reporting/tests/test_reporting.rs | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/reporting/tests/test_reporting.rs b/reporting/tests/test_reporting.rs index 6a5ba6a1e3..ea9df598eb 100644 --- a/reporting/tests/test_reporting.rs +++ b/reporting/tests/test_reporting.rs @@ -8442,4 +8442,48 @@ I need all branches in an `if` to have the same type! ), ) } + + #[test] + fn let_polymorphism_with_scoped_type_variables() { + report_problem_as( + indoc!( + r#" + f : a -> a + f = \x -> + y : a -> a + y = \z -> z + + n = y 1u8 + x1 = y x + (\_ -> x1) n + + f + "# + ), + indoc!( + r#" + ── TYPE MISMATCH ─────────────────────────────────────────────────────────────── + + The 1st argument to `y` is not what I expect: + + 6│ n = y 1u8 + ^^^ + + This argument is an integer of type: + + U8 + + But `y` needs the 1st argument to be: + + a + + Tip: The type annotation uses the type variable `a` to say that this + definition can produce any type of value. But in the body I see that + it will only produce a `U8` value of a single specific type. Maybe + change the type annotation to be more specific? Maybe change the code + to be more general? + "# + ), + ) + } } From 53962a4799a1b004550e44a8bd678550e2606f27 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 17:27:33 +0100 Subject: [PATCH 131/132] mark infer variables as flex --- compiler/constrain/src/expr.rs | 112 ++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 31 deletions(-) diff --git a/compiler/constrain/src/expr.rs b/compiler/constrain/src/expr.rs index 7273f7caa3..0a97de4327 100644 --- a/compiler/constrain/src/expr.rs +++ b/compiler/constrain/src/expr.rs @@ -1329,19 +1329,21 @@ fn constrain_def( def_pattern_state.vars.push(expr_var); - let mut new_rigids = Vec::new(); - let expr_con = match &def.annotation { + match &def.annotation { Some(annotation) => { let arity = annotation.signature.arity(); let rigids = &env.rigids; let mut ftv = rigids.clone(); - let signature = instantiate_rigids( + let InstantiateRigids { + signature, + new_rigid_variables, + new_infer_variables, + } = instantiate_rigids( &annotation.signature, &annotation.introduced_variables, - &mut new_rigids, - &mut ftv, &def.loc_pattern, + &mut ftv, &mut def_pattern_state.headers, ); @@ -1513,8 +1515,16 @@ fn constrain_def( closure_constraint, ]; - let and_constraint = constraints.and_constraint(cons); - constraints.exists(vars, and_constraint) + let expr_con = constraints.exists_many(vars, cons); + + constrain_def_make_constraint( + constraints, + new_rigid_variables, + new_infer_variables, + expr_con, + body_con, + def_pattern_state, + ) } _ => { @@ -1539,35 +1549,62 @@ fn constrain_def( // Store type into AST vars. We use Store so errors aren't reported twice constraints.store(signature, expr_var, std::file!(), std::line!()), ]; - constraints.and_constraint(cons) + let expr_con = constraints.and_constraint(cons); + + constrain_def_make_constraint( + constraints, + new_rigid_variables, + new_infer_variables, + expr_con, + body_con, + def_pattern_state, + ) } } } None => { // no annotation, so no extra work with rigids - constrain_expr( + let expr_con = constrain_expr( constraints, env, def.loc_expr.region, &def.loc_expr.value, NoExpectation(expr_type), + ); + + constrain_def_make_constraint( + constraints, + vec![], + vec![], + expr_con, + body_con, + def_pattern_state, ) } - }; + } +} +fn constrain_def_make_constraint( + constraints: &mut Constraints, + new_rigid_variables: Vec, + new_infer_variables: Vec, + expr_con: Constraint, + body_con: Constraint, + def_pattern_state: PatternState, +) -> Constraint { let and_constraint = constraints.and_constraint(def_pattern_state.constraints); let def_con = constraints.let_constraint( [], - [], + new_infer_variables, SendMap::default(), // empty, because our functions have no arguments! and_constraint, expr_con, ); constraints.let_constraint( - new_rigids, + new_rigid_variables, def_pattern_state.vars, def_pattern_state.headers, def_con, @@ -1631,17 +1668,23 @@ fn constrain_closure_size( constraints.and_constraint(captured_symbols_constraints) } +pub struct InstantiateRigids { + pub signature: Type, + pub new_rigid_variables: Vec, + pub new_infer_variables: Vec, +} + fn instantiate_rigids( annotation: &Type, introduced_vars: &IntroducedVariables, - new_rigids: &mut Vec, - ftv: &mut MutMap, // rigids defined before the current annotation loc_pattern: &Loc, + ftv: &mut MutMap, // rigids defined before the current annotation headers: &mut SendMap>, -) -> Type { +) -> InstantiateRigids { let mut annotation = annotation.clone(); - let mut rigid_substitution: ImMap = ImMap::default(); + let mut new_rigid_variables = Vec::new(); + let mut rigid_substitution: ImMap = ImMap::default(); for (name, var) in introduced_vars.var_by_name.iter() { use std::collections::hash_map::Entry::*; @@ -1653,19 +1696,18 @@ fn instantiate_rigids( Vacant(vacant) => { // It's possible to use this rigid in nested defs vacant.insert(*var); - new_rigids.push(*var); + new_rigid_variables.push(*var); } } } // wildcards are always freshly introduced in this annotation - new_rigids.extend(introduced_vars.wildcards.iter().copied()); - - // inferred vars are always freshly introduced in this annotation - new_rigids.extend(introduced_vars.inferred.iter().copied()); + new_rigid_variables.extend(introduced_vars.wildcards.iter().copied()); // lambda set vars are always freshly introduced in this annotation - new_rigids.extend(introduced_vars.lambda_sets.iter().copied()); + new_rigid_variables.extend(introduced_vars.lambda_sets.iter().copied()); + + let new_infer_variables = introduced_vars.inferred.clone(); // Instantiate rigid variables if !rigid_substitution.is_empty() { @@ -1681,7 +1723,11 @@ fn instantiate_rigids( headers.extend(new_headers) } - annotation + InstantiateRigids { + signature: annotation, + new_rigid_variables, + new_infer_variables, + } } fn constrain_recursive_defs( @@ -1744,17 +1790,21 @@ pub fn rec_defs_help( Some(annotation) => { let arity = annotation.signature.arity(); let mut ftv = env.rigids.clone(); - let mut new_rigids = Vec::new(); - let signature = instantiate_rigids( + let InstantiateRigids { + signature, + new_rigid_variables, + new_infer_variables, + } = instantiate_rigids( &annotation.signature, &annotation.introduced_variables, - &mut new_rigids, - &mut ftv, &def.loc_pattern, + &mut ftv, &mut def_pattern_state.headers, ); + flex_info.vars.extend(new_infer_variables); + let annotation_expected = FromAnnotation( def.loc_pattern.clone(), arity, @@ -1912,10 +1962,10 @@ pub fn rec_defs_help( let and_constraint = constraints.and_constraint(cons); let def_con = constraints.exists(vars, and_constraint); - rigid_info.vars.extend(&new_rigids); + rigid_info.vars.extend(&new_rigid_variables); rigid_info.constraints.push(constraints.let_constraint( - new_rigids, + new_rigid_variables, def_pattern_state.vars, SendMap::default(), // no headers introduced (at this level) def_con, @@ -1947,10 +1997,10 @@ pub fn rec_defs_help( ]; let def_con = constraints.and_constraint(cons); - rigid_info.vars.extend(&new_rigids); + rigid_info.vars.extend(&new_rigid_variables); rigid_info.constraints.push(constraints.let_constraint( - new_rigids, + new_rigid_variables, def_pattern_state.vars, SendMap::default(), // no headers introduced (at this level) def_con, From 1b6624730322d6468a258084f6d9dc0de1a76bb7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sat, 5 Mar 2022 19:30:15 +0100 Subject: [PATCH 132/132] renaming --- compiler/solve/src/solve.rs | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index b82994af92..7a4efb1e48 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -222,12 +222,17 @@ enum Work<'a> { constraint: &'a Constraint, }, CheckForInfiniteTypes(LocalDefVarsVec<(Symbol, Loc)>), - LetConSimple { + /// The ret_con part of a let constraint that does NOT introduces rigid and/or flex variables + LetConNoVariables { env: &'a Env, rank: Rank, let_con: &'a LetConstraint, }, - LetConComplex { + /// The ret_con part of a let constraint that introduces rigid and/or flex variables + /// + /// These introduced variables must be generalized, hence this variant + /// is more complex than `LetConNoVariables`. + LetConIntroducesVariables { env: &'a Env, rank: Rank, let_con: &'a LetConstraint, @@ -274,7 +279,7 @@ fn solve( continue; } - Work::LetConSimple { env, rank, let_con } => { + Work::LetConNoVariables { env, rank, let_con } => { // NOTE be extremely careful with shadowing here let offset = let_con.defs_and_ret_constraint.index(); let ret_constraint = &constraints.constraints[offset + 1]; @@ -298,18 +303,16 @@ fn solve( stack.push(Work::Constraint { env: arena.alloc(new_env), rank, - constraint: &ret_constraint, + constraint: ret_constraint, }); continue; } - Work::LetConComplex { env, rank, let_con } => { + Work::LetConIntroducesVariables { env, rank, let_con } => { // NOTE be extremely careful with shadowing here let offset = let_con.defs_and_ret_constraint.index(); let ret_constraint = &constraints.constraints[offset + 1]; - let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; - let next_rank = rank.next(); let mark = state.mark; @@ -361,6 +364,8 @@ fn solve( // inference, and does not come from elm. It's unclear whether this is // a bug with uniqueness inference (something is redundant that // shouldn't be) or that it just never came up in elm. + let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; + let failing: Vec<_> = rigid_vars .iter() .filter(|&var| !subs.redundant(*var) && subs.get_rank(*var) != Rank::NONE) @@ -392,7 +397,7 @@ fn solve( stack.push(Work::Constraint { env: arena.alloc(new_env), rank, - constraint: &ret_constraint, + constraint: ret_constraint, }); state = state_for_ret_con; @@ -627,14 +632,14 @@ fn solve( let rigid_vars = &constraints.variables[let_con.rigid_vars.indices()]; if matches!(&ret_constraint, True) && let_con.rigid_vars.is_empty() { - introduce(subs, rank, pools, &flex_vars); + introduce(subs, rank, pools, flex_vars); // If the return expression is guaranteed to solve, // solve the assignments themselves and move on. stack.push(Work::Constraint { env, rank, - constraint: &defs_constraint, + constraint: defs_constraint, }); state @@ -644,11 +649,11 @@ fn solve( // // Note that the LetConSimple gets the current env and rank, // and not the env/rank from after solving the defs_constraint - stack.push(Work::LetConSimple { env, rank, let_con }); + stack.push(Work::LetConNoVariables { env, rank, let_con }); stack.push(Work::Constraint { env, rank, - constraint: &defs_constraint, + constraint: defs_constraint, }); state @@ -686,11 +691,11 @@ fn solve( // // Note that the LetConSimple gets the current env and rank, // and not the env/rank from after solving the defs_constraint - stack.push(Work::LetConComplex { env, rank, let_con }); + stack.push(Work::LetConIntroducesVariables { env, rank, let_con }); stack.push(Work::Constraint { env, rank: next_rank, - constraint: &defs_constraint, + constraint: defs_constraint, }); state