diff --git a/.github/workflows/nix_linux_x86_64.yml b/.github/workflows/nix_linux_x86_64.yml index a536a1b740..5c49c9b379 100644 --- a/.github/workflows/nix_linux_x86_64.yml +++ b/.github/workflows/nix_linux_x86_64.yml @@ -18,13 +18,23 @@ jobs: run: nix-build - name: execute tests with --release - run: nix develop -c cargo test --locked --release + # skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step + run: nix develop -c cargo test --locked --release -- --skip glue_cli_tests + + - name: glue_cli_tests + # single threaded due to difficult bug when multithreading + run: nix develop -c cargo test --locked --release glue_cli_tests -- --test-threads=1 - name: roc test all builtins run: nix develop -c ./ci/roc_test_builtins.sh - name: test wasm32 cli_tests - run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" + # skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step + run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" -- --skip glue_cli_tests + + - name: wasm32 glue_cli_tests + # single threaded due to difficult bug when multithreading + run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" glue_cli_tests -- --test-threads=1 - name: test the dev backend # these tests require an explicit feature flag run: nix develop -c cargo nextest run --locked --release --package test_gen --no-default-features --features gen-dev --no-fail-fast diff --git a/.gitignore b/.gitignore index 9497a4f7c9..04a6c6e09c 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ metadata #editors .idea/ .vscode/ +.helix/ .ignore .exrc .vimrc diff --git a/Cargo.lock b/Cargo.lock index 287ce67f37..e4d0918599 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2354,6 +2354,7 @@ dependencies = [ "roc_types", "soa", "static_assertions", + "test_compile", "ven_pretty", ] @@ -2630,7 +2631,6 @@ dependencies = [ "cli_test_utils", "dircpy", "fnv", - "indexmap", "indoc", "libc", "libloading", @@ -2954,7 +2954,6 @@ dependencies = [ "rustyline-derive", "target-lexicon", "tempfile", - "unicode-segmentation", ] [[package]] @@ -3140,6 +3139,36 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "roc_specialize_types" +version = "0.0.1" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "bumpalo", + "hashbrown", + "indoc", + "parking_lot", + "pretty_assertions", + "roc_builtins", + "roc_can", + "roc_collections", + "roc_derive", + "roc_load", + "roc_module", + "roc_parse", + "roc_problem", + "roc_region", + "roc_reporting", + "roc_solve", + "roc_target", + "roc_types", + "soa", + "static_assertions", + "test_compile", + "test_solve_helpers", +] + [[package]] name = "roc_std" version = "0.0.1" @@ -3781,6 +3810,30 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test_compile" +version = "0.0.1" +dependencies = [ + "bumpalo", + "pretty_assertions", + "roc_builtins", + "roc_can", + "roc_collections", + "roc_constrain", + "roc_derive", + "roc_load", + "roc_module", + "roc_parse", + "roc_problem", + "roc_region", + "roc_reporting", + "roc_solve", + "roc_solve_problem", + "roc_specialize_types", + "roc_target", + "roc_types", +] + [[package]] name = "test_derive" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index dc6e58b7b1..fbc064f2a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "crates/repl_wasm", "crates/repl_expect", "crates/roc_std", + "crates/test_compile", "crates/test_utils", "crates/test_utils_dir", "crates/valgrind_tests", @@ -75,11 +76,9 @@ version = "0.0.1" inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [ "llvm16-0", ] } - soa = { path = "crates/soa" } - +roc_specialize_types = { path = "crates/compiler/specialize_types" } arrayvec = "0.7.2" # update roc_std/Cargo.toml on change -backtrace = "0.3.67" base64-url = "1.4.13" bincode = "1.3.3" bitflags = "1.3.2" @@ -87,9 +86,7 @@ bitvec = "1.0.1" blake3 = "1.3.3" brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli bumpalo = { version = "3.12.0", features = ["collections"] } -bytemuck = { version = "1.13.1", features = ["derive"] } capstone = { version = "0.11.0", default-features = false } -cgmath = "0.18.0" chrono = "0.4.26" clap = { version = "4.2.7", default-features = false, features = [ "std", @@ -99,10 +96,8 @@ clap = { version = "4.2.7", default-features = false, features = [ "usage", "error-context", ] } -colored = "2.0.0" console_error_panic_hook = "0.1.7" const_format = { version = "0.2.30", features = ["const_generics"] } -copypasta = "0.8.2" criterion = { git = "https://github.com/Anton-4/criterion.rs", features = [ "html_reports", ], rev = "30ea0c5" } @@ -116,7 +111,6 @@ flate2 = "1.0.25" fnv = "1.0.7" fs_extra = "1.3.0" futures = "0.3.26" -glyph_brush = "0.7.7" hashbrown = { version = "0.14.3" } iced-x86 = { version = "1.18.0", default-features = false, features = [ "std", @@ -126,7 +120,6 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [ ] } im = "15.1.0" im-rc = "15.1.0" -indexmap = "2.1.0" indoc = "1.0.9" insta = "1.28.0" js-sys = "0.3.61" @@ -140,18 +133,13 @@ mach_object = "0.1" maplit = "1.0.2" memmap2 = "0.5.10" mimalloc = { version = "0.1.34", default-features = false } -nonempty = "0.8.1" object = { version = "0.32.2", default-features = false, features = [ "read", "write", ] } packed_struct = "0.10.1" -page_size = "0.5.0" -palette = "0.6.1" parking_lot = "0.12" perfcnt = "0.8.0" -pest = "2.5.6" -pest_derive = "2.5.6" pretty_assertions = "1.3.0" # update roc_std/Cargo.toml on change proc-macro2 = "1.0.63" proptest = "1.1.0" @@ -185,21 +173,17 @@ syn = { version = "1.0.109", features = ["full", "extra-traits"] } tar = "0.4.38" target-lexicon = "0.12.6" tempfile = "=3.2.0" -threadpool = "1.8.1" tracing = { version = "0.1.40", features = ["release_max_level_off"] } tracing-appender = "0.2.2" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -unicode-segmentation = "1.10.1" -uuid = { version = "1.3.0", features = ["v4"] } walkdir = "2.3.2" wasm-bindgen = "0.2.84" wasm-bindgen-futures = "0.4.34" -wgpu = "0.12.0" -wgpu_glyph = "0.16.0" -winapi = { version = "0.3.9", features = ["memoryapi"] } -winit = "0.26.1" wyhash = "0.5.0" +# Testing +test_compile = { path = "crates/test_compile" } + # Optimizations based on https://deterministic.space/high-performance-rust.html [profile.release] codegen-units = 1 diff --git a/crates/compiler/can/Cargo.toml b/crates/compiler/can/Cargo.toml index 12ff46d750..ebf1973cdc 100644 --- a/crates/compiler/can/Cargo.toml +++ b/crates/compiler/can/Cargo.toml @@ -30,3 +30,4 @@ soa.workspace = true indoc.workspace = true insta.workspace = true pretty_assertions.workspace = true +test_compile.workspace = true diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index 5ade8cb07f..8c18990a27 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -132,7 +132,7 @@ pub struct AbleVariable { pub first_seen: Region, } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct IntroducedVariables { pub wildcards: Vec>, pub lambda_sets: Vec, diff --git a/crates/compiler/can/src/constraint.rs b/crates/compiler/can/src/constraint.rs index 1a6572c4d4..d71d56f0c0 100644 --- a/crates/compiler/can/src/constraint.rs +++ b/crates/compiler/can/src/constraint.rs @@ -929,7 +929,7 @@ pub struct FxExpectation { pub ann_region: Option, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum FxCallKind { Call(Option), Stmt, @@ -943,7 +943,7 @@ pub struct FxSuffixConstraint { pub region: Region, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum FxSuffixKind { Let(Symbol), Pattern(Symbol), @@ -957,9 +957,16 @@ impl FxSuffixKind { Self::UnsuffixedRecordField => IdentSuffix::None, } } + + pub fn symbol(&self) -> Option<&Symbol> { + match self { + Self::Let(symbol) | Self::Pattern(symbol) => Some(symbol), + Self::UnsuffixedRecordField => None, + } + } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum ExpectEffectfulReason { Stmt, Ignored, diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index dce1e4d658..4d935b3442 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -63,7 +63,7 @@ use std::io::Read; use std::path::PathBuf; use std::sync::Arc; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Def { pub loc_pattern: Loc, pub loc_expr: Loc, @@ -91,7 +91,7 @@ impl Def { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum DefKind { /// A def that introduces identifiers Let, @@ -123,7 +123,7 @@ impl DefKind { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Annotation { pub signature: Type, pub introduced_variables: IntroducedVariables, diff --git a/crates/compiler/can/src/expected.rs b/crates/compiler/can/src/expected.rs index d1fae08746..1b6facae9e 100644 --- a/crates/compiler/can/src/expected.rs +++ b/crates/compiler/can/src/expected.rs @@ -2,7 +2,7 @@ use crate::pattern::Pattern; use roc_region::all::{Loc, Region}; use roc_types::types::{AnnotationSource, PReason, Reason}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Expected { NoExpectation(T), FromAnnotation(Loc, usize, AnnotationSource, T), @@ -10,7 +10,7 @@ pub enum Expected { } /// Like Expected, but for Patterns. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum PExpected { NoExpectation(T), ForReason(PReason, T, Region), diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 18eac0f773..da73977f86 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -85,7 +85,55 @@ impl Display for IntValue { } } -#[derive(Clone, Debug)] +impl IntValue { + pub fn as_u8(self) -> u8 { + self.as_u128() as u8 + } + + pub fn as_i8(self) -> i8 { + self.as_i128() as i8 + } + + pub fn as_u16(self) -> u16 { + self.as_u128() as u16 + } + + pub fn as_i16(self) -> i16 { + self.as_i128() as i16 + } + + pub fn as_u32(self) -> u32 { + self.as_u128() as u32 + } + + pub fn as_i32(self) -> i32 { + self.as_i128() as i32 + } + + pub fn as_u64(self) -> u64 { + self.as_u128() as u64 + } + + pub fn as_i64(self) -> i64 { + self.as_i128() as i64 + } + + pub fn as_u128(self) -> u128 { + match self { + IntValue::I128(i128) => i128::from_ne_bytes(i128) as u128, + IntValue::U128(u128) => u128::from_ne_bytes(u128), + } + } + + pub fn as_i128(self) -> i128 { + match self { + IntValue::I128(i128) => i128::from_ne_bytes(i128), + IntValue::U128(u128) => u128::from_ne_bytes(u128) as i128, + } + } +} + +#[derive(Clone, Debug, PartialEq)] pub enum Expr { // Literals @@ -104,7 +152,7 @@ pub enum Expr { loc_elems: Vec>, }, - // An ingested files, it's bytes, and the type variable. + // An ingested files, its bytes, and the type variable. IngestedFile(Box, Arc>, Variable), // Lookups @@ -131,7 +179,7 @@ pub enum Expr { /// The actual condition of the when expression. loc_cond: Box>, cond_var: Variable, - /// Result type produced by the branches. + /// Type of each branch (and therefore the type of the entire `when` expression) expr_var: Variable, region: Region, /// The branches of the when, and the type of the condition that they expect to be matched @@ -292,7 +340,7 @@ pub enum Expr { RuntimeError(RuntimeError), } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct ExpectLookup { pub symbol: Symbol, pub var: Variable, @@ -370,7 +418,7 @@ impl Expr { /// Stores exhaustiveness-checking metadata for a closure argument that may /// have an annotated type. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct AnnotatedMark { pub annotation_var: Variable, pub exhaustive: ExhaustiveMark, @@ -394,7 +442,7 @@ impl AnnotatedMark { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ClosureData { pub function_type: Variable, pub closure_type: Variable, @@ -491,7 +539,7 @@ impl StructAccessorData { /// An opaque wrapper like `@Foo`, which is equivalent to `\p -> @Foo p` /// These are desugared to closures, but we distinguish them so we can have /// better error messages during constraint generation. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct OpaqueWrapFunctionData { pub opaque_name: Symbol, pub opaque_var: Variable, @@ -563,7 +611,7 @@ impl OpaqueWrapFunctionData { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Field { pub var: Variable, // The region of the full `foo: f bar`, rather than just `f bar` @@ -587,7 +635,7 @@ impl Recursive { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct WhenBranchPattern { pub pattern: Loc, /// Degenerate branch patterns are those that don't fully bind symbols that the branch body @@ -596,7 +644,7 @@ pub struct WhenBranchPattern { pub degenerate: bool, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct WhenBranch { pub patterns: Vec, pub value: Loc, diff --git a/crates/compiler/can/src/pattern.rs b/crates/compiler/can/src/pattern.rs index 153cb83a0f..0cf1ece726 100644 --- a/crates/compiler/can/src/pattern.rs +++ b/crates/compiler/can/src/pattern.rs @@ -19,7 +19,7 @@ use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type}; /// A pattern, including possible problems (e.g. shadowing) so that /// codegen can generate a runtime error if this pattern is reached. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum Pattern { Identifier(Symbol), As(Box>, Symbol), @@ -198,7 +198,7 @@ impl Pattern { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ListPatterns { pub patterns: Vec>, /// Where a rest pattern splits patterns before and after it, if it does at all. @@ -207,6 +207,7 @@ pub struct ListPatterns { /// [ .., A, B ] -> patterns = [A, B], rest = 0 /// [ A, .., B ] -> patterns = [A, B], rest = 1 /// [ A, B, .. ] -> patterns = [A, B], rest = 2 + /// Optionally, the rest pattern can be named - e.g. `[ A, B, ..others ]` pub opt_rest: Option<(usize, Option>)>, } @@ -228,7 +229,7 @@ impl ListPatterns { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct RecordDestruct { pub var: Variable, pub label: Lowercase, @@ -236,14 +237,14 @@ pub struct RecordDestruct { pub typ: DestructType, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct TupleDestruct { pub var: Variable, pub destruct_index: usize, pub typ: (Variable, Loc), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum DestructType { Required, Optional(Variable, Loc), diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap index 29b6f33f3a..6ba9b8e9ce 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-28, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -65,10 +66,10 @@ Defs { @15-22, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap index 63b7cf74b3..8349f24751 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-25, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -49,10 +50,10 @@ Defs { @15-19, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap index d202ad0630..d9db4e765a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-43, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -59,10 +60,10 @@ Defs { @15-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap index 6eae91bfb9..ac8e3c7f0b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-45, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -59,10 +60,10 @@ Defs { @15-35, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap index 5f52ead865..b70a801863 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-28, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -49,10 +50,10 @@ Defs { @15-22, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index ceecd20482..e43dd481ff 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -39,10 +40,10 @@ Defs { @11-14, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap index 2e3be19c34..bbe698549d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-42, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -49,10 +50,10 @@ Defs { @16-35, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index e7e9b93321..9e57e9e0bf 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -11,10 +11,10 @@ Defs { @0-69, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -34,10 +34,10 @@ Defs { @15-57, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -67,10 +67,10 @@ Defs { @31-43, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index 064c592e05..283fc71c0d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-114, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @39-101, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -84,10 +85,10 @@ Defs { @82-91, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index b68387bd88..7269bfe039 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-143, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @56-119, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -112,10 +113,10 @@ Defs { @76-83, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -181,10 +182,10 @@ Defs { @92-99, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap index 73b5f70e41..e671cbec8b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_expr.snap @@ -1,19 +1,20 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @0-26, + @0-28, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -24,88 +25,90 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-26 Defs( + @11-28 Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @15-26, + @16-27, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], value_defs: [ Body( - @15-26 Identifier { + @16-27 Identifier { ident: "1", }, - @15-26 ParensAround( + @16-27 ParensAround( Defs( Defs { tags: [ EitherIndex(2147483648), ], regions: [ - @20-25, + @21-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], value_defs: [ Body( - @20-25 Identifier { + @21-26 Identifier { ident: "0", }, - @20-25 Apply( - @22-23 Var { - module_name: "Num", - ident: "add", - }, - [ - @20-21 Num( - "1", + @21-26 ParensAround( + Apply( + @23-24 Var { + module_name: "Num", + ident: "add", + }, + [ + @21-22 Num( + "1", + ), + @25-26 Num( + "1", + ), + ], + BinOp( + Plus, ), - @24-25 Num( - "1", - ), - ], - BinOp( - Plus, ), ), ), ], }, - @15-26 LowLevelDbg( + @16-27 LowLevelDbg( ( "test.roc:3", " ", ), - @20-25 Apply( - @20-25 Var { + @21-26 Apply( + @21-26 Var { module_name: "Inspect", ident: "toStr", }, [ - @20-25 Var { + @21-26 Var { module_name: "", ident: "0", }, ], Space, ), - @20-25 Var { + @21-26 Var { module_name: "", ident: "0", }, @@ -115,25 +118,25 @@ Defs { ), ], }, - @11-26 LowLevelDbg( + @11-28 LowLevelDbg( ( "test.roc:2", - "in =\n ", + "n =\n ", ), - @15-26 Apply( - @15-26 Var { + @16-27 Apply( + @16-27 Var { module_name: "Inspect", ident: "toStr", }, [ - @15-26 Var { + @16-27 Var { module_name: "", ident: "1", }, ], Space, ), - @15-26 Var { + @16-27 Var { module_name: "", ident: "1", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap index c983f084a2..c6e36e9cc7 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-49, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap index 4b96a43719..7c505c58a7 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-24, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap index 7c28faa163..9398768a22 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-99, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index 97bba1ffc9..2c8f41d387 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -12,12 +13,12 @@ Defs { @56-98, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, @@ -39,10 +40,10 @@ Defs { @15-20, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -74,10 +75,10 @@ Defs { @25-39, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -151,10 +152,10 @@ Defs { @75-80, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap index 5b864ea68f..972c298388 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-158, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -45,10 +46,10 @@ Defs { @50-52, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap index 55ff449979..1562686d0a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-31, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @11-24, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index 46bac26dc1..22b4f957cb 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-307, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -37,14 +38,14 @@ Defs { @109-298, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, ], spaces: [ Newline, @@ -171,10 +172,10 @@ Defs { @140-152, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -293,10 +294,10 @@ Defs { @227-239, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap index 616b0f261c..8c0bd2adf9 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-189, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -35,12 +36,12 @@ Defs { @52-70, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap index 550d2c84de..8b33ad83f2 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7081.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -14,14 +15,14 @@ Defs { @229-266, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, + Slice { start: 2, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 1 }, ], spaces: [ Newline, @@ -120,12 +121,12 @@ Defs { @203-208, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap index 0815495897..21efe374ca 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__issue_7103.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -12,12 +13,12 @@ Defs { @35-45, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap index aa775291e9..6de13d2d05 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @15-17, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index 86f8ef922c..c40fb213b1 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -39,10 +40,10 @@ Defs { @11-14, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -99,10 +100,10 @@ Defs { @20-23, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap index 9df21857a3..f493cbcb22 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index a72d226a2e..6c1b1b859a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -11,10 +11,10 @@ Defs { @0-72, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -40,10 +40,10 @@ Defs { @11-23, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap index 2f6e899c9f..745fabf31a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-51, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @17-24, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index 316b51bdce..6f2633394c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-24, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -39,10 +40,10 @@ Defs { @11-16, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap index 1d656ab5af..d0fff1ca95 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-61, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap index 3e93218668..a78050a314 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-49, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @23-42, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap index 9479304f98..d9cfc3dbc0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-22, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap index f8c6c4404c..947dc76d84 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__pizza_dbg.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-51, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -33,10 +34,10 @@ Defs { @11-40, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -60,10 +61,10 @@ Defs { @11-12, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index ff13f20710..5b304a726c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -11,10 +11,10 @@ Defs { @0-73, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -40,10 +40,10 @@ Defs { @11-57, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index 23024cc973..e8edbf8406 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-67, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -48,10 +49,10 @@ Defs { @19-30, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap index 2e648dd0fd..b8f0160911 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-154, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index 19735343ba..8e3fac63bc 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-44, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -45,10 +46,10 @@ Defs { @28-29, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap index 115d307815..e2f330e05b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-45, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index dda750d1dd..3f1ca0ac0e 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-120, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -67,10 +68,10 @@ Defs { @54-65, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap index fbe8232063..d3ef8f9e9c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap @@ -1,6 +1,7 @@ --- source: crates/compiler/can/tests/test_suffixed.rs expression: snapshot +snapshot_kind: text --- Defs { tags: [ @@ -10,10 +11,10 @@ Defs { @0-74, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/can/tests/test_can_expr.rs b/crates/compiler/can/tests/test_can_expr.rs new file mode 100644 index 0000000000..76d4a23078 --- /dev/null +++ b/crates/compiler/can/tests/test_can_expr.rs @@ -0,0 +1,13 @@ +#[cfg(test)] +mod test_can_expr { + use roc_can::expr::Expr; + use test_compile::can_expr; + + #[test] + fn test_can_unit() { + let output = can_expr("{}"); + + assert_eq!(output.problems, Vec::new()); + assert!(matches!(output.expr, Expr::EmptyRecord)); + } +} diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs index 2a30c718d0..df260403cb 100644 --- a/crates/compiler/can/tests/test_suffixed.rs +++ b/crates/compiler/can/tests/test_suffixed.rs @@ -464,7 +464,7 @@ mod suffixed_tests { run_test!( r#" main = - dbg (dbg 1 + 1) + dbg (dbg (1 + 1)) "# ); } diff --git a/crates/compiler/collections/src/lib.rs b/crates/compiler/collections/src/lib.rs index 09af7b0b44..36850f2fa7 100644 --- a/crates/compiler/collections/src/lib.rs +++ b/crates/compiler/collections/src/lib.rs @@ -4,6 +4,7 @@ #![allow(clippy::large_enum_variant)] pub mod all; +mod push; mod reference_matrix; mod small_string_interner; mod small_vec; @@ -12,6 +13,7 @@ mod vec_map; mod vec_set; pub use all::{default_hasher, BumpMap, ImEntry, ImMap, ImSet, MutMap, MutSet, SendMap}; +pub use push::Push; pub use reference_matrix::{ReferenceMatrix, Sccs, TopologicalSort}; pub use small_string_interner::SmallStringInterner; pub use small_vec::SmallVec; diff --git a/crates/compiler/collections/src/push.rs b/crates/compiler/collections/src/push.rs new file mode 100644 index 0000000000..c3fb5cf2d2 --- /dev/null +++ b/crates/compiler/collections/src/push.rs @@ -0,0 +1,15 @@ +pub trait Push { + fn push(&mut self, entry: T); +} + +impl Push for Vec { + fn push(&mut self, entry: T) { + self.push(entry); + } +} + +impl Push for &mut Vec { + fn push(&mut self, entry: T) { + (*self).push(entry); + } +} diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index 3921cf218e..cba4129398 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -64,6 +64,15 @@ pub struct Env { } impl Env { + pub fn new(home: ModuleId) -> Self { + Self { + rigids: MutMap::default(), + resolutions_to_make: Vec::new(), + home, + fx_expectation: None, + } + } + pub fn with_fx_expectation( &mut self, fx_var: Variable, diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 8f7d92d84f..d4a2a999b8 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -447,7 +447,7 @@ impl<'a> Formattable for Expr<'a> { buf.push_str("dbg"); } DbgStmt(condition, continuation) => { - fmt_dbg_stmt(buf, condition, continuation, self.is_multiline(), indent); + fmt_dbg_stmt(buf, condition, continuation, parens, indent); } LowLevelDbg(_, _, _) => unreachable!( "LowLevelDbg should only exist after desugaring, not during formatting" @@ -1022,42 +1022,15 @@ fn fmt_dbg_stmt<'a>( buf: &mut Buf, condition: &'a Loc>, continuation: &'a Loc>, - _: bool, + parens: Parens, indent: u16, ) { - buf.ensure_ends_with_newline(); - buf.indent(indent); - buf.push_str("dbg"); - - buf.spaces(1); - - fn should_outdent(mut expr: &Expr) -> bool { - loop { - match expr { - Expr::ParensAround(_) | Expr::List(_) | Expr::Record(_) | Expr::Tuple(_) => { - return true - } - Expr::SpaceAfter(inner, _) => { - expr = inner; - } - _ => return false, - } - } - } - - let inner_indent = if should_outdent(&condition.value) { - indent - } else { - indent + INDENT - }; - - let cond_value = condition.value.extract_spaces(); - - let is_defs = matches!(cond_value.item, Expr::Defs(_, _)); - - let newlines = if is_defs { Newlines::Yes } else { Newlines::No }; - - condition.format_with_options(buf, Parens::NotNeeded, newlines, inner_indent); + Expr::Apply( + &Loc::at_zero(Expr::Dbg), + &[condition], + called_via::CalledVia::Space, + ) + .format_with_options(buf, parens, Newlines::Yes, indent); // Always put a blank line after the `dbg` line(s) buf.ensure_ends_with_blank_line(); diff --git a/crates/compiler/gen_dev/src/generic64/aarch64.rs b/crates/compiler/gen_dev/src/generic64/aarch64.rs index 71c937a3e4..6a01e5524b 100644 --- a/crates/compiler/gen_dev/src/generic64/aarch64.rs +++ b/crates/compiler/gen_dev/src/generic64/aarch64.rs @@ -1846,6 +1846,26 @@ impl Assembler for AArch64Assembler { neg_reg64_reg64(buf, dst, src); } + #[inline(always)] + fn neg_freg64_freg64( + buf: &mut Vec<'_, u8>, + _relocs: &mut Vec<'_, Relocation>, + dst: AArch64FloatReg, + src: AArch64FloatReg, + ) { + fneg_freg_freg(buf, FloatWidth::F64, dst, src); + } + + #[inline(always)] + fn neg_freg32_freg32( + buf: &mut Vec<'_, u8>, + _relocs: &mut Vec<'_, Relocation>, + dst: AArch64FloatReg, + src: AArch64FloatReg, + ) { + fneg_freg_freg(buf, FloatWidth::F32, dst, src); + } + #[inline(always)] fn sub_reg64_reg64_imm32( buf: &mut Vec<'_, u8>, @@ -3953,6 +3973,24 @@ fn fsub_freg_freg_freg( buf.extend(inst.bytes()); } +/// `FNEG Sd/Dd, Sn/Dn` +#[inline(always)] +fn fneg_freg_freg( + buf: &mut Vec<'_, u8>, + ftype: FloatWidth, + dst: AArch64FloatReg, + src: AArch64FloatReg, +) { + let inst = + FloatingPointDataProcessingOneSource::new(FloatingPointDataProcessingOneSourceParams { + ptype: ftype, + opcode: 0b00010, + rn: src, + rd: dst, + }); + buf.extend(inst.bytes()); +} + /// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags. #[inline(always)] fn fcmp_freg_freg( diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index 8d6482a5d3..a73beadb24 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -557,6 +557,18 @@ pub trait Assembler: Sized + Copy { fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg); fn neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg); + fn neg_freg64_freg64( + buf: &mut Vec<'_, u8>, + relocs: &mut Vec<'_, Relocation>, + dst: FloatReg, + src: FloatReg, + ); + fn neg_freg32_freg32( + buf: &mut Vec<'_, u8>, + relocs: &mut Vec<'_, Relocation>, + dst: FloatReg, + src: FloatReg, + ); fn mul_freg32_freg32_freg32( buf: &mut Vec<'_, u8>, dst: FloatReg, @@ -1791,7 +1803,24 @@ impl< let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src); ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg); } - x => todo!("NumNeg: layout, {:?}", x), + LayoutRepr::F32 => { + let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst); + let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); + ASM::neg_freg32_freg32(&mut self.buf, &mut self.relocs, dst_reg, src_reg); + } + LayoutRepr::F64 => { + let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst); + let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src); + ASM::neg_freg64_freg64(&mut self.buf, &mut self.relocs, dst_reg, src_reg); + } + LayoutRepr::DEC => self.build_fn_call( + dst, + bitcode::DEC_NEGATE.to_string(), + &[*src], + &[Layout::DEC], + &Layout::DEC, + ), + other => internal_error!("unreachable: NumNeg for layout, {:?}", other), } } diff --git a/crates/compiler/gen_dev/src/generic64/x86_64.rs b/crates/compiler/gen_dev/src/generic64/x86_64.rs index f03310886a..73cb0f418c 100644 --- a/crates/compiler/gen_dev/src/generic64/x86_64.rs +++ b/crates/compiler/gen_dev/src/generic64/x86_64.rs @@ -2602,6 +2602,28 @@ impl Assembler for X86_64Assembler { neg_reg64(buf, dst); } + #[inline(always)] + fn neg_freg64_freg64( + buf: &mut Vec<'_, u8>, + relocs: &mut Vec<'_, Relocation>, + dst: X86_64FloatReg, + src: X86_64FloatReg, + ) { + Self::mov_freg64_imm64(buf, relocs, dst, f64::from_bits(0x8000_0000_0000_0000)); + xorpd_freg64_freg64(buf, dst, src); + } + + #[inline(always)] + fn neg_freg32_freg32( + buf: &mut Vec<'_, u8>, + relocs: &mut Vec<'_, Relocation>, + dst: X86_64FloatReg, + src: X86_64FloatReg, + ) { + Self::mov_freg32_imm32(buf, relocs, dst, f32::from_bits(0x8000_0000)); + xorps_freg32_freg32(buf, dst, src); + } + #[inline(always)] fn sub_reg64_reg64_imm32( buf: &mut Vec<'_, u8>, @@ -3352,6 +3374,49 @@ fn sqrtss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64F } } +/// `XORPD xmm1, xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1. +#[inline(always)] +fn xorpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { + let dst_high = dst as u8 > 7; + let dst_mod = dst as u8 % 8; + + let src_high = src as u8 > 7; + let src_mod = src as u8 % 8; + + if dst_high || src_high { + buf.extend([ + 0x66, + 0x40 | ((dst_high as u8) << 2) | (src_high as u8), + 0x0F, + 0x57, + 0xC0 | (dst_mod << 3) | src_mod, + ]) + } else { + buf.extend([0x66, 0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]); + } +} + +/// `XORPS xmm1,xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1. +#[inline(always)] +fn xorps_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) { + let dst_high = dst as u8 > 7; + let dst_mod = dst as u8 % 8; + + let src_high = src as u8 > 7; + let src_mod = src as u8 % 8; + + if dst_high || src_high { + buf.extend([ + 0x40 | ((dst_high as u8) << 2) | (src_high as u8), + 0x0F, + 0x57, + 0xC0 | (dst_mod << 3) | src_mod, + ]); + } else { + buf.extend([0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]); + } +} + /// `TEST r/m64,r64` -> AND r64 with r/m64; set SF, ZF, PF according to result. #[allow(dead_code)] #[inline(always)] diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index 12e2975306..2d7f3caf4e 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -2241,6 +2241,7 @@ fn build_dec_unary_op<'a, 'ctx>( match op { NumAbs => dec_unary_op(env, bitcode::DEC_ABS, arg), + NumNeg => dec_unary_op(env, bitcode::DEC_NEGATE, arg), NumAcos => dec_unary_op(env, bitcode::DEC_ACOS, arg), NumAsin => dec_unary_op(env, bitcode::DEC_ASIN, arg), NumAtan => dec_unary_op(env, bitcode::DEC_ATAN, arg), diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 16fa30bb5d..a17f97e93c 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -1624,6 +1624,7 @@ impl<'a> LowLevelCall<'a> { } F32 => backend.code_builder.f32_neg(), F64 => backend.code_builder.f64_neg(), + Decimal => self.load_args_and_call_zig(backend, bitcode::DEC_NEGATE), _ => todo!("{:?} for {:?}", self.lowlevel, self.ret_layout), } } diff --git a/crates/compiler/load/tests/helpers/mod.rs b/crates/compiler/load/tests/helpers/mod.rs index 4e3d5af447..89a3c6236e 100644 --- a/crates/compiler/load/tests/helpers/mod.rs +++ b/crates/compiler/load/tests/helpers/mod.rs @@ -53,6 +53,7 @@ pub fn infer_expr( function_kind: FunctionKind::LambdaSet, module_params: None, module_params_vars: Default::default(), + host_exposed_symbols: None, #[cfg(debug_assertions)] checkmate: None, }; @@ -189,6 +190,24 @@ pub fn can_expr_with<'a>( // rules multiple times unnecessarily. let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr); + let mut scope = Scope::new( + home, + "TestPath".into(), + IdentIds::default(), + Default::default(), + ); + + let dep_idents = IdentIds::exposed_builtins(0); + let mut env = Env::new( + arena, + expr_str, + home, + Path::new("Test.roc"), + &dep_idents, + &module_ids, + None, + roc_can::env::FxMode::PurityInference, + ); let (loc_expr, output) = canonicalize_expr( &mut env, &mut var_store, diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 590abbfa25..541e3a04be 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -11054,8 +11054,8 @@ All branches in an `if` must have the same type! 4│ Recursive := [Infinitely Recursive] ^^^^^^^^^ - Recursion in opaquees is only allowed if recursion happens behind a - tagged union, at least one variant of which is not recursive. + Recursion in opaque types is only allowed if recursion happens behind + a tagged union, at least one variant of which is not recursive. " ); @@ -14812,7 +14812,7 @@ All branches in an `if` must have the same type! Str.trim msg "# ), - @r###" + @r#" ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ────────────────────────────── This call to `Effect.putLine!` might produce an effect: @@ -14829,18 +14829,7 @@ All branches in an `if` must have the same type! You can still run the program with this error, which can be helpful when you're debugging. - - ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── - - This function is pure, but its name suggests otherwise: - - 5│ main! = \{} -> - ^^^^^ - - The exclamation mark at the end is reserved for effectful functions. - - Hint: Did you forget to run an effect? Is the type annotation wrong? - "### + "# ); test_report!( @@ -15423,7 +15412,7 @@ All branches in an `if` must have the same type! pureHigherOrder = \f, x -> f x "# ), - @r###" + @r#" ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── This 1st argument to `pureHigherOrder` has an unexpected type: @@ -15438,18 +15427,7 @@ All branches in an `if` must have the same type! But `pureHigherOrder` needs its 1st argument to be: Str -> {} - - ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── - - This function is pure, but its name suggests otherwise: - - 5│ main! = \{} -> - ^^^^^ - - The exclamation mark at the end is reserved for effectful functions. - - Hint: Did you forget to run an effect? Is the type annotation wrong? - "### + "# ); test_report!( @@ -15467,7 +15445,7 @@ All branches in an `if` must have the same type! pureHigherOrder = \f, x -> f x "# ), - @r###" + @r#" ── TYPE MISMATCH in /code/proj/Main.roc ──────────────────────────────────────── This 1st argument to `pureHigherOrder` has an unexpected type: @@ -15482,17 +15460,6 @@ All branches in an `if` must have the same type! But `pureHigherOrder` needs its 1st argument to be: Str -> {} - - ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ────────────────────────────── - - This function is pure, but its name suggests otherwise: - - 5│ main! = \{} -> - ^^^^^ - - The exclamation mark at the end is reserved for effectful functions. - - Hint: Did you forget to run an effect? Is the type annotation wrong? - "### + "# ); } diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index ff7ce3a50c..1899315113 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -350,6 +350,8 @@ fn start_phase<'a>( None }; + let is_host_exposed = state.root_id == module.module_id; + BuildTask::solve_module( module, ident_ids, @@ -367,6 +369,7 @@ fn start_phase<'a>( state.cached_types.clone(), derived_module, state.exec_mode, + is_host_exposed, // #[cfg(debug_assertions)] checkmate, @@ -922,6 +925,7 @@ enum BuildTask<'a> { cached_subs: CachedTypeState, derived_module: SharedDerivedModule, exec_mode: ExecutionMode, + is_host_exposed: bool, #[cfg(debug_assertions)] checkmate: Option, @@ -4331,6 +4335,7 @@ impl<'a> BuildTask<'a> { cached_subs: CachedTypeState, derived_module: SharedDerivedModule, exec_mode: ExecutionMode, + is_host_exposed: bool, #[cfg(debug_assertions)] checkmate: Option, ) -> Self { @@ -4355,6 +4360,7 @@ impl<'a> BuildTask<'a> { cached_subs, derived_module, exec_mode, + is_host_exposed, #[cfg(debug_assertions)] checkmate, @@ -4661,6 +4667,7 @@ fn run_solve_solve( var_store: VarStore, module: Module, derived_module: SharedDerivedModule, + is_host_exposed: bool, #[cfg(debug_assertions)] checkmate: Option, ) -> SolveResult { @@ -4711,6 +4718,12 @@ fn run_solve_solve( let (solve_output, solved_implementations, exposed_vars_by_symbol) = { let module_id = module.module_id; + let host_exposed_idents = if is_host_exposed { + Some(&exposed_symbols) + } else { + None + }; + let solve_config = SolveConfig { home: module_id, types, @@ -4724,6 +4737,7 @@ fn run_solve_solve( checkmate, module_params, module_params_vars: imported_param_vars, + host_exposed_symbols: host_exposed_idents, }; let solve_output = roc_solve::module::run_solve( @@ -4800,6 +4814,7 @@ fn run_solve<'a>( cached_types: CachedTypeState, derived_module: SharedDerivedModule, exec_mode: ExecutionMode, + is_host_exposed: bool, #[cfg(debug_assertions)] checkmate: Option, ) -> Msg<'a> { @@ -4831,6 +4846,7 @@ fn run_solve<'a>( var_store, module, derived_module, + is_host_exposed, // #[cfg(debug_assertions)] checkmate, @@ -4863,6 +4879,7 @@ fn run_solve<'a>( var_store, module, derived_module, + is_host_exposed, // #[cfg(debug_assertions)] checkmate, @@ -6256,6 +6273,7 @@ fn run_task<'a>( cached_subs, derived_module, exec_mode, + is_host_exposed, #[cfg(debug_assertions)] checkmate, @@ -6275,6 +6293,7 @@ fn run_task<'a>( cached_subs, derived_module, exec_mode, + is_host_exposed, // #[cfg(debug_assertions)] checkmate, diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 7fd20333a8..fa10aefa51 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -35,7 +35,7 @@ use roc_region::all::{Loc, Position, Region}; use crate::parser::Progress::{self, *}; -fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> { +pub fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> { |_arena, state: State<'a>, _min_indent: u32| { if state.has_reached_end() { Ok((NoProgress, (), state)) @@ -545,10 +545,6 @@ fn stmt_start<'a>( EExpr::Expect, expect_help(options, preceding_comment) )), - loc(specialize_err( - EExpr::Dbg, - dbg_stmt_help(options, preceding_comment) - )), loc(specialize_err(EExpr::Return, return_help(options))), loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))), map( @@ -2668,34 +2664,6 @@ fn return_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Stmt<'a>, ERetu .trace("return_help") } -fn dbg_stmt_help<'a>( - options: ExprParseOptions, - preceding_comment: Region, -) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> { - (move |arena: &'a Bump, state: State<'a>, min_indent| { - let (_, _, state) = - parser::keyword(keyword::DBG, EExpect::Dbg).parse(arena, state, min_indent)?; - - let (_, condition, state) = parse_block( - options, - arena, - state, - true, - EExpect::IndentCondition, - EExpect::Condition, - ) - .map_err(|(_, f)| (MadeProgress, f))?; - - let stmt = Stmt::ValueDef(ValueDef::Dbg { - condition: arena.alloc(condition), - preceding_comment, - }); - - Ok((MadeProgress, stmt, state)) - }) - .trace("dbg_stmt_help") -} - fn dbg_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpect<'a>> { (move |arena: &'a Bump, state: State<'a>, min_indent: u32| { let (_, _, next_state) = @@ -3110,12 +3078,43 @@ fn stmts_to_defs<'a>( } Stmt::Expr(e) => { if i + 1 < stmts.len() { - defs.push_value_def( - ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))), - sp_stmt.item.region, - sp_stmt.before, - &[], - ); + if let Expr::Apply( + Loc { + value: Expr::Dbg, .. + }, + args, + _, + ) = e + { + if args.len() != 1 { + // TODO: this should be done in can, not parsing! + return Err(EExpr::Dbg( + EExpect::DbgArity(sp_stmt.item.region.start()), + sp_stmt.item.region.start(), + )); + } + let condition = &args[0]; + let rest = stmts_to_expr(&stmts[i + 1..], arena)?; + let e = Expr::DbgStmt(condition, arena.alloc(rest)); + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + last_expr = Some(Loc::at(sp_stmt.item.region, e)); + + // don't re-process the rest of the statements; they got consumed by the dbg expr + break; + } else { + defs.push_value_def( + ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))), + sp_stmt.item.region, + sp_stmt.before, + &[], + ); + } } else { let e = if sp_stmt.before.is_empty() { e diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 6e79d807ba..6c5780ad8a 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -1465,6 +1465,7 @@ impl<'a> Normalize<'a> for EExpect<'a> { EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero()) } EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()), + EExpect::DbgArity(_) => EExpect::DbgArity(Position::zero()), } } } diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index 2eb972b18e..e21cfb222b 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -513,6 +513,7 @@ pub enum EExpect<'a> { Condition(&'a EExpr<'a>, Position), Continuation(&'a EExpr<'a>, Position), IndentCondition(Position), + DbgArity(Position), } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index e8a5e3a0b6..721e4b6c25 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -6,7 +6,7 @@ use roc_can::constraint::{Constraint, Constraints}; use roc_can::expr::PendingDerives; use roc_can::module::{ExposedByModule, ModuleParams, ResolvedImplementations, RigidVariables}; use roc_collections::all::MutMap; -use roc_collections::VecMap; +use roc_collections::{VecMap, VecSet}; use roc_derive::SharedDerivedModule; use roc_error_macros::internal_error; use roc_module::symbol::{ModuleId, Symbol}; @@ -76,6 +76,8 @@ pub struct SolveConfig<'a> { /// Needed during solving to resolve lambda sets from derived implementations that escape into /// the user module. pub derived_module: SharedDerivedModule, + /// Symbols that are exposed to the host which might need special treatment. + pub host_exposed_symbols: Option<&'a VecSet>, #[cfg(debug_assertions)] /// The checkmate collector for this module. diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index ca0df7cabe..e45907f8ca 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -19,7 +19,7 @@ use roc_can::constraint::{ }; use roc_can::expected::{Expected, PExpected}; use roc_can::module::ModuleParams; -use roc_collections::VecMap; +use roc_collections::{VecMap, VecSet}; use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; @@ -136,6 +136,7 @@ fn run_help( function_kind, module_params, module_params_vars, + host_exposed_symbols, .. } = config; @@ -190,6 +191,7 @@ fn run_help( &mut awaiting_specializations, module_params, module_params_vars, + host_exposed_symbols, ); RunSolveOutput { @@ -249,6 +251,7 @@ fn solve( awaiting_specializations: &mut AwaitingSpecializations, module_params: Option, module_params_vars: VecMap, + host_exposed_symbols: Option<&VecSet>, ) -> State { let scope = Scope::new(module_params); @@ -455,6 +458,7 @@ fn solve( solve_suffix_fx( env, problems, + host_exposed_symbols, FxSuffixKind::Let(*symbol), loc_var.value, &loc_var.region, @@ -853,7 +857,7 @@ fn solve( *type_index, ); - solve_suffix_fx(env, problems, *kind, actual, region); + solve_suffix_fx(env, problems, host_exposed_symbols, *kind, actual, region); state } ExpectEffectful(variable, reason, region) => { @@ -1625,6 +1629,7 @@ fn solve( fn solve_suffix_fx( env: &mut InferenceEnv<'_>, problems: &mut Vec, + host_exposed_symbols: Option<&VecSet>, kind: FxSuffixKind, variable: Variable, region: &Region, @@ -1651,7 +1656,16 @@ fn solve_suffix_fx( let fx = *fx; match env.subs.get_content_without_compacting(fx) { Content::Pure => { - problems.push(TypeError::SuffixedPureFunction(*region, kind)); + match (kind.symbol(), host_exposed_symbols) { + (Some(sym), Some(host_exposed)) if host_exposed.contains(sym) => { + // If exposed to the platform, it's allowed to be suffixed but pure + // The platform might require a `main!` function that could perform + // effects, but that's not a requirement. + } + _ => { + problems.push(TypeError::SuffixedPureFunction(*region, kind)); + } + } } Content::FlexVar(_) => { env.subs.set_content(fx, Content::Effectful); diff --git a/crates/compiler/solve_problem/src/lib.rs b/crates/compiler/solve_problem/src/lib.rs index 37affa703c..2bffc88166 100644 --- a/crates/compiler/solve_problem/src/lib.rs +++ b/crates/compiler/solve_problem/src/lib.rs @@ -15,7 +15,7 @@ use roc_region::all::Region; use roc_types::types::{Category, ErrorType, PatternCategory}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum TypeError { BadExpr(Region, Category, ErrorType, Expected), BadPattern(Region, PatternCategory, ErrorType, PExpected), diff --git a/crates/compiler/specialize_types/Cargo.toml b/crates/compiler/specialize_types/Cargo.toml new file mode 100644 index 0000000000..93c306122c --- /dev/null +++ b/crates/compiler/specialize_types/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "roc_specialize_types" +description = "Convert a type-checked canonical IR to a monomorphized IR by creating specializations of all functions, such that they are monomorphic in types. We will specialize again later after computing lambda sets, which happens after this pass." + +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +[dependencies] +roc_can = { path = "../can" } +roc_region = { path = "../region" } +roc_types = { path = "../types" } +roc_collections = { path = "../collections" } +roc_module = { path = "../module" } +roc_solve = { path = "../solve" } + +bitvec.workspace = true +arrayvec.workspace = true +bumpalo.workspace = true +hashbrown.workspace = true +parking_lot.workspace = true +static_assertions.workspace = true +indoc.workspace = true + +soa.workspace = true + +[dev-dependencies] +roc_builtins = { path = "../builtins" } +roc_derive = { path = "../derive", features = ["debug-derived-symbols"] } +roc_load = { path = "../load" } +roc_parse = { path = "../parse" } +roc_problem = { path = "../problem" } +roc_reporting = { path = "../../reporting" } +roc_target = { path = "../roc_target" } +roc_solve = { path = "../solve" } +test_solve_helpers = { path = "../test_solve_helpers" } + +test_compile.workspace = true +pretty_assertions.workspace = true diff --git a/crates/compiler/specialize_types/src/debug_info.rs b/crates/compiler/specialize_types/src/debug_info.rs new file mode 100644 index 0000000000..4283a978b7 --- /dev/null +++ b/crates/compiler/specialize_types/src/debug_info.rs @@ -0,0 +1,8 @@ +#[derive(Default)] +pub struct DebugInfo; + +impl DebugInfo { + pub fn new() -> Self { + DebugInfo + } +} diff --git a/crates/compiler/specialize_types/src/expr.rs b/crates/compiler/specialize_types/src/expr.rs new file mode 100644 index 0000000000..c258dcf104 --- /dev/null +++ b/crates/compiler/specialize_types/src/expr.rs @@ -0,0 +1,651 @@ +use roc_can::expr::{ + ClosureData, Expr, Field, OpaqueWrapFunctionData, StructAccessorData, WhenBranch, + WhenBranchPattern, +}; +use roc_region::all::Loc; +use roc_types::subs::{ + AliasVariables, Content, Descriptor, FlatType, LambdaSet, RecordFields, Subs, SubsSlice, + TupleElems, UnionLabels, Variable, VariableSubsSlice, +}; +use roc_types::types::{Type, Uls}; + +pub fn monomorphize(expr: Loc, subs: &mut Subs) -> Loc { + Loc { + region: expr.region, + value: monomorphize_expr(expr.value, subs), + } +} + +fn monomorphize_expr(expr: Expr, subs: &mut Subs) -> Expr { + match expr { + Expr::Num(var, str, int_value, bound) => { + Expr::Num(monomorphize_var(var, subs), str, int_value, bound) + } + Expr::Int(var1, var2, str, int_value, bound) => Expr::Int( + monomorphize_var(var1, subs), + monomorphize_var(var2, subs), + str, + int_value, + bound, + ), + Expr::Float(var1, var2, str, float_value, bound) => Expr::Float( + monomorphize_var(var1, subs), + monomorphize_var(var2, subs), + str, + float_value, + bound, + ), + Expr::Str(s) => Expr::Str(s), + Expr::IngestedFile(path, bytes, var) => { + Expr::IngestedFile(path, bytes, monomorphize_var(var, subs)) + } + Expr::SingleQuote(var1, var2, c, bound) => Expr::SingleQuote( + monomorphize_var(var1, subs), + monomorphize_var(var2, subs), + c, + bound, + ), + Expr::List { + elem_var, + loc_elems, + } => Expr::List { + elem_var: monomorphize_var(elem_var, subs), + loc_elems: loc_elems + .into_iter() + .map(|loc_elem| monomorphize(loc_elem, subs)) + .collect(), + }, + Expr::Var(symbol, var) => Expr::Var(symbol, monomorphize_var(var, subs)), + Expr::ParamsVar { + symbol, + var, + params_symbol, + params_var, + } => Expr::ParamsVar { + symbol, + var: monomorphize_var(var, subs), + params_symbol, + params_var: monomorphize_var(params_var, subs), + }, + Expr::AbilityMember(symbol, spec_id, var) => { + Expr::AbilityMember(symbol, spec_id, monomorphize_var(var, subs)) + } + Expr::When { + cond_var, + expr_var, + region, + loc_cond, + branches, + branches_cond_var, + exhaustive, + } => Expr::When { + cond_var: monomorphize_var(cond_var, subs), + expr_var: monomorphize_var(expr_var, subs), + region, + loc_cond: Box::new(monomorphize(*loc_cond, subs)), + branches: branches + .into_iter() + .map(|branch| monomorphize_when_branch(branch, subs)) + .collect(), + branches_cond_var: monomorphize_var(branches_cond_var, subs), + exhaustive, + }, + Expr::If { + cond_var, + branch_var, + branches, + final_else, + } => Expr::If { + cond_var: monomorphize_var(cond_var, subs), + branch_var: monomorphize_var(branch_var, subs), + branches: branches + .into_iter() + .map(|(cond, expr)| (monomorphize(cond, subs), monomorphize(expr, subs))) + .collect(), + final_else: Box::new(monomorphize(*final_else, subs)), + }, + Expr::LetRec(defs, expr, cycle_mark) => Expr::LetRec( + defs.into_iter() + .map(|def| monomorphize_def(def, subs)) + .collect(), + Box::new(monomorphize(*expr, subs)), + cycle_mark, + ), + Expr::LetNonRec(def, expr) => Expr::LetNonRec( + Box::new(monomorphize_def(*def, subs)), + Box::new(monomorphize(*expr, subs)), + ), + Expr::Call(boxed, args, called_via) => { + let (fn_var, loc_expr, lambda_set_var, ret_var) = *boxed; + Expr::Call( + Box::new(( + monomorphize_var(fn_var, subs), + monomorphize(loc_expr, subs), + monomorphize_var(lambda_set_var, subs), + monomorphize_var(ret_var, subs), + )), + args.into_iter() + .map(|(var, loc_expr)| { + (monomorphize_var(var, subs), monomorphize(loc_expr, subs)) + }) + .collect(), + called_via, + ) + } + Expr::Closure(closure_data) => Expr::Closure(monomorphize_closure_data(closure_data, subs)), + Expr::Record { record_var, fields } => Expr::Record { + record_var: monomorphize_var(record_var, subs), + fields: fields + .into_iter() + .map(|(k, v)| (k, monomorphize_field(v, subs))) + .collect(), + }, + Expr::EmptyRecord => Expr::EmptyRecord, + Expr::Tuple { tuple_var, elems } => Expr::Tuple { + tuple_var: monomorphize_var(tuple_var, subs), + elems: elems + .into_iter() + .map(|(var, loc_expr)| { + ( + monomorphize_var(var, subs), + Box::new(monomorphize(*loc_expr, subs)), + ) + }) + .collect(), + }, + Expr::ImportParams(module_id, region, params) => Expr::ImportParams( + module_id, + region, + params.map(|(var, expr)| { + ( + monomorphize_var(var, subs), + Box::new(monomorphize_expr(*expr, subs)), + ) + }), + ), + Expr::Crash { msg, ret_var } => Expr::Crash { + msg: Box::new(monomorphize(*msg, subs)), + ret_var: monomorphize_var(ret_var, subs), + }, + Expr::RecordAccess { + record_var, + ext_var, + field_var, + loc_expr, + field, + } => Expr::RecordAccess { + record_var: monomorphize_var(record_var, subs), + ext_var: monomorphize_var(ext_var, subs), + field_var: monomorphize_var(field_var, subs), + loc_expr: Box::new(monomorphize(*loc_expr, subs)), + field, + }, + Expr::RecordAccessor(data) => { + Expr::RecordAccessor(monomorphize_struct_accessor_data(data, subs)) + } + Expr::TupleAccess { + tuple_var, + ext_var, + elem_var, + loc_expr, + index, + } => Expr::TupleAccess { + tuple_var: monomorphize_var(tuple_var, subs), + ext_var: monomorphize_var(ext_var, subs), + elem_var: monomorphize_var(elem_var, subs), + loc_expr: Box::new(monomorphize(*loc_expr, subs)), + index, + }, + Expr::RecordUpdate { + record_var, + ext_var, + symbol, + updates, + } => Expr::RecordUpdate { + record_var: monomorphize_var(record_var, subs), + ext_var: monomorphize_var(ext_var, subs), + symbol, + updates: updates + .into_iter() + .map(|(k, v)| (k, monomorphize_field(v, subs))) + .collect(), + }, + Expr::Tag { + tag_union_var, + ext_var, + name, + arguments, + } => Expr::Tag { + tag_union_var: monomorphize_var(tag_union_var, subs), + ext_var: monomorphize_var(ext_var, subs), + name, + arguments: arguments + .into_iter() + .map(|(var, loc_expr)| (monomorphize_var(var, subs), monomorphize(loc_expr, subs))) + .collect(), + }, + Expr::ZeroArgumentTag { + closure_name, + variant_var, + ext_var, + name, + } => Expr::ZeroArgumentTag { + closure_name, + variant_var: monomorphize_var(variant_var, subs), + ext_var: monomorphize_var(ext_var, subs), + name, + }, + Expr::OpaqueRef { + opaque_var, + name, + argument, + specialized_def_type, + type_arguments, + lambda_set_variables, + } => Expr::OpaqueRef { + opaque_var: monomorphize_var(opaque_var, subs), + name, + argument: Box::new(( + monomorphize_var(argument.0, subs), + monomorphize(argument.1, subs), + )), + specialized_def_type: Box::new(monomorphize_type(*specialized_def_type, subs)), + type_arguments, + lambda_set_variables, + }, + Expr::OpaqueWrapFunction(data) => { + Expr::OpaqueWrapFunction(monomorphize_opaque_wrap_function_data(data, subs)) + } + Expr::Expect { + loc_condition, + loc_continuation, + lookups_in_cond, + } => Expr::Expect { + loc_condition: Box::new(monomorphize(*loc_condition, subs)), + loc_continuation: Box::new(monomorphize(*loc_continuation, subs)), + lookups_in_cond, + }, + Expr::ExpectFx { + loc_condition, + loc_continuation, + lookups_in_cond, + } => Expr::ExpectFx { + loc_condition: Box::new(monomorphize(*loc_condition, subs)), + loc_continuation: Box::new(monomorphize(*loc_continuation, subs)), + lookups_in_cond, + }, + Expr::Dbg { + source_location, + source, + loc_message, + loc_continuation, + variable, + symbol, + } => Expr::Dbg { + source_location, + source, + loc_message: Box::new(monomorphize(*loc_message, subs)), + loc_continuation: Box::new(monomorphize(*loc_continuation, subs)), + variable: monomorphize_var(variable, subs), + symbol, + }, + Expr::TypedHole(var) => Expr::TypedHole(monomorphize_var(var, subs)), + Expr::RuntimeError(error) => Expr::RuntimeError(error), + Expr::RunLowLevel { op, args, ret_var } => Expr::RunLowLevel { + op, + args: args + .into_iter() + .map(|(var, expr)| (monomorphize_var(var, subs), monomorphize_expr(expr, subs))) + .collect(), + ret_var: monomorphize_var(ret_var, subs), + }, + Expr::ForeignCall { + foreign_symbol, + args, + ret_var, + } => Expr::ForeignCall { + foreign_symbol, + args: args + .into_iter() + .map(|(var, expr)| (monomorphize_var(var, subs), monomorphize_expr(expr, subs))) + .collect(), + ret_var: monomorphize_var(ret_var, subs), + }, + } +} + +fn monomorphize_var(var: Variable, subs: &mut Subs) -> Variable { + let root = subs.get_root_key_without_compacting(var); + let content = subs.get_content_without_compacting(root).clone(); + + match content { + Content::Structure(flat_type) => match flat_type { + FlatType::Apply(symbol, args) => { + let new_args: Vec = args + .into_iter() + .map(|arg| monomorphize_var(subs[arg], subs)) + .collect(); + let new_slice = VariableSubsSlice::insert_into_subs(subs, new_args); + let new_flat_type = FlatType::Apply(symbol, new_slice); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::Func(args, closure_var, ret_var) => { + let new_args: Vec = args + .into_iter() + .map(|arg| monomorphize_var(subs[arg], subs)) + .collect(); + let new_args_slice = VariableSubsSlice::insert_into_subs(subs, new_args); + let new_closure_var = monomorphize_var(closure_var, subs); + let new_ret_var = monomorphize_var(ret_var, subs); + let new_flat_type = FlatType::Func(new_args_slice, new_closure_var, new_ret_var); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::Record(record_fields, ext_var) => { + let new_variables: Vec = record_fields + .variables() + .into_iter() + .map(|v| monomorphize_var(subs[v], subs)) + .collect(); + let new_variables_slice = VariableSubsSlice::insert_into_subs(subs, new_variables); + let new_record_fields = RecordFields { + length: record_fields.length, + field_names_start: record_fields.field_names_start, + variables_start: new_variables_slice.start, + field_types_start: record_fields.field_types_start, + }; + let new_ext_var = monomorphize_var(ext_var, subs); + let new_flat_type = FlatType::Record(new_record_fields, new_ext_var); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::Tuple(tuple_elems, ext_var) => { + let new_variables: Vec = tuple_elems + .variables() + .into_iter() + .map(|v| monomorphize_var(subs[v], subs)) + .collect(); + let new_variables_slice = VariableSubsSlice::insert_into_subs(subs, new_variables); + let new_tuple_elems = TupleElems { + length: tuple_elems.length, + elem_index_start: tuple_elems.elem_index_start, + variables_start: new_variables_slice.start, + }; + let new_ext_var = monomorphize_var(ext_var, subs); + let new_flat_type = FlatType::Tuple(new_tuple_elems, new_ext_var); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::TagUnion(union_labels, tag_ext) => { + let new_variable_slices = + SubsSlice::reserve_variable_slices(subs, union_labels.len()); + for (old_slice_index, new_slice_index) in union_labels + .variables() + .into_iter() + .zip(new_variable_slices) + { + let old_slice = subs[old_slice_index]; + let new_variables: Vec = old_slice + .into_iter() + .map(|v| monomorphize_var(subs[v], subs)) + .collect(); + let new_slice = VariableSubsSlice::insert_into_subs(subs, new_variables); + subs[new_slice_index] = new_slice; + } + let new_union_labels = + UnionLabels::from_slices(union_labels.labels(), new_variable_slices); + let new_tag_ext = tag_ext.map(|v| monomorphize_var(v, subs)); + let new_flat_type = FlatType::TagUnion(new_union_labels, new_tag_ext); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::FunctionOrTagUnion(tag_names, symbols, tag_ext) => { + let new_tag_ext = tag_ext.map(|v| monomorphize_var(v, subs)); + let new_flat_type = FlatType::FunctionOrTagUnion(tag_names, symbols, new_tag_ext); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::RecursiveTagUnion(rec_var, union_labels, tag_ext) => { + let new_rec_var = monomorphize_var(rec_var, subs); + let new_variable_slices = + SubsSlice::reserve_variable_slices(subs, union_labels.len()); + for (old_slice_index, new_slice_index) in union_labels + .variables() + .into_iter() + .zip(new_variable_slices) + { + let old_slice = subs[old_slice_index]; + let new_variables: Vec = old_slice + .into_iter() + .map(|v| monomorphize_var(subs[v], subs)) + .collect(); + let new_slice = VariableSubsSlice::insert_into_subs(subs, new_variables); + subs[new_slice_index] = new_slice; + } + let new_union_labels = + UnionLabels::from_slices(union_labels.labels(), new_variable_slices); + let new_tag_ext = tag_ext.map(|v| monomorphize_var(v, subs)); + let new_flat_type = + FlatType::RecursiveTagUnion(new_rec_var, new_union_labels, new_tag_ext); + subs.fresh(Descriptor::from(Content::Structure(new_flat_type))) + } + FlatType::EmptyRecord | FlatType::EmptyTuple | FlatType::EmptyTagUnion => var, + }, + Content::Alias(symbol, alias_variables, aliased_var, alias_kind) => { + let new_variables: Vec = alias_variables + .all_variables() + .into_iter() + .map(|v| monomorphize_var(subs[v], subs)) + .collect(); + let new_variables_slice = VariableSubsSlice::insert_into_subs(subs, new_variables); + let new_alias_variables = AliasVariables { + variables_start: new_variables_slice.start, + all_variables_len: alias_variables.all_variables_len, + lambda_set_variables_len: alias_variables.lambda_set_variables_len, + type_variables_len: alias_variables.type_variables_len, + }; + let new_aliased_var = monomorphize_var(aliased_var, subs); + let new_content = + Content::Alias(symbol, new_alias_variables, new_aliased_var, alias_kind); + subs.fresh(Descriptor::from(new_content)) + } + Content::LambdaSet(lambda_set) => { + let new_solved_slices = + SubsSlice::reserve_variable_slices(subs, lambda_set.solved.len()); + for (old_slice_index, new_slice_index) in lambda_set + .solved + .variables() + .into_iter() + .zip(new_solved_slices) + { + let old_slice = subs[old_slice_index]; + let new_variables: Vec = old_slice + .into_iter() + .map(|v| monomorphize_var(subs[v], subs)) + .collect(); + let new_slice = VariableSubsSlice::insert_into_subs(subs, new_variables); + subs[new_slice_index] = new_slice; + } + let new_solved = + UnionLabels::from_slices(lambda_set.solved.labels(), new_solved_slices); + let new_recursion_var = lambda_set.recursion_var.map(|v| monomorphize_var(v, subs)); + let new_unspecialized = + SubsSlice::reserve_uls_slice(subs, lambda_set.unspecialized.len()); + for (i, uls) in lambda_set.unspecialized.into_iter().enumerate() { + let Uls(var, sym, region) = subs[uls]; + let new_var = monomorphize_var(var, subs); + + if let Some(i) = new_unspecialized.into_iter().nth(i) { + subs[i] = Uls(new_var, sym, region); + } else { + debug_panic!("new_unspecialized is too short"); + } + } + let new_ambient_function = monomorphize_var(lambda_set.ambient_function, subs); + let new_lambda_set = LambdaSet { + solved: new_solved, + recursion_var: new_recursion_var, + unspecialized: new_unspecialized, + ambient_function: new_ambient_function, + }; + let new_content = Content::LambdaSet(new_lambda_set); + subs.fresh(Descriptor::from(new_content)) + } + Content::FlexVar(_) + | Content::RigidVar(_) + | Content::FlexAbleVar(_, _) + | Content::RigidAbleVar(_, _) + | Content::RecursionVar { .. } + | Content::ErasedLambda + | Content::RangedNumber(_) + | Content::Error => var, + } +} + +fn monomorphize_when_branch(branch: WhenBranch, subs: &mut Subs) -> WhenBranch { + WhenBranch { + patterns: branch + .patterns + .into_iter() + .map(|p| WhenBranchPattern { + pattern: p.pattern, + degenerate: p.degenerate, + }) + .collect(), + value: monomorphize(branch.value, subs), + guard: branch.guard.map(|g| monomorphize(g, subs)), + redundant: branch.redundant, + } +} + +fn monomorphize_def(def: roc_can::def::Def, subs: &mut Subs) -> roc_can::def::Def { + roc_can::def::Def { + loc_pattern: def.loc_pattern, + loc_expr: monomorphize(def.loc_expr, subs), + expr_var: monomorphize_var(def.expr_var, subs), + pattern_vars: def + .pattern_vars + .into_iter() + .map(|(k, v)| (k, monomorphize_var(v, subs))) + .collect(), + annotation: def.annotation.map(|a| monomorphize_annotation(a, subs)), + } +} + +fn monomorphize_closure_data(data: ClosureData, subs: &mut Subs) -> ClosureData { + ClosureData { + function_type: monomorphize_var(data.function_type, subs), + closure_type: monomorphize_var(data.closure_type, subs), + return_type: monomorphize_var(data.return_type, subs), + name: data.name, + captured_symbols: data + .captured_symbols + .into_iter() + .map(|(s, v)| (s, monomorphize_var(v, subs))) + .collect(), + recursive: data.recursive, + arguments: data + .arguments + .into_iter() + .map(|(v, m, p)| (monomorphize_var(v, subs), m, p)) + .collect(), + loc_body: Box::new(monomorphize(*data.loc_body, subs)), + } +} + +fn monomorphize_field(field: Field, subs: &mut Subs) -> Field { + Field { + var: monomorphize_var(field.var, subs), + region: field.region, + loc_expr: Box::new(monomorphize(*field.loc_expr, subs)), + } +} + +fn monomorphize_struct_accessor_data( + data: StructAccessorData, + subs: &mut Subs, +) -> StructAccessorData { + StructAccessorData { + name: data.name, + function_var: monomorphize_var(data.function_var, subs), + record_var: monomorphize_var(data.record_var, subs), + closure_var: monomorphize_var(data.closure_var, subs), + ext_var: monomorphize_var(data.ext_var, subs), + field_var: monomorphize_var(data.field_var, subs), + field: data.field, + } +} + +fn monomorphize_opaque_wrap_function_data( + data: OpaqueWrapFunctionData, + subs: &mut Subs, +) -> OpaqueWrapFunctionData { + OpaqueWrapFunctionData { + opaque_name: data.opaque_name, + opaque_var: monomorphize_var(data.opaque_var, subs), + specialized_def_type: monomorphize_type(data.specialized_def_type, subs), + type_arguments: data.type_arguments, + lambda_set_variables: data.lambda_set_variables, + function_name: data.function_name, + function_var: monomorphize_var(data.function_var, subs), + argument_var: monomorphize_var(data.argument_var, subs), + closure_var: monomorphize_var(data.closure_var, subs), + } +} + +fn monomorphize_annotation( + annotation: roc_can::def::Annotation, + subs: &mut Subs, +) -> roc_can::def::Annotation { + roc_can::def::Annotation { + signature: monomorphize_type(annotation.signature, subs), + introduced_variables: annotation.introduced_variables, + aliases: annotation.aliases, + region: annotation.region, + } +} + +fn monomorphize_type(typ: Type, subs: &mut Subs) -> Type { + match typ { + Type::Tuple(elems, ext) => Type::Tuple( + elems + .into_iter() + .map(|(idx, elem)| (idx, monomorphize_type(elem, subs))) + .collect(), + ext, + ), + Type::Record(fields, ext) => Type::Record( + fields + .into_iter() + .map(|(name, field_type)| { + (name, field_type.map(|t| monomorphize_type(t.clone(), subs))) + }) + .collect(), + ext, + ), + Type::Apply(name, args, region) => Type::Apply( + name, + args.into_iter() + .map(|arg| arg.map(|t| monomorphize_type(t.clone(), subs))) + .collect(), + region, + ), + Type::Function(args, ret, ext) => Type::Function( + args.into_iter() + .map(|arg| monomorphize_type(arg, subs)) + .collect(), + Box::new(monomorphize_type(*ret, subs)), + ext, + ), + Type::TagUnion(tags, ext) => Type::TagUnion( + tags.into_iter() + .map(|(name, tag_types)| { + ( + name, + tag_types + .into_iter() + .map(|t| monomorphize_type(t, subs)) + .collect(), + ) + }) + .collect(), + ext, + ), + other => other, + } +} diff --git a/crates/compiler/specialize_types/src/foreign_symbol.rs b/crates/compiler/specialize_types/src/foreign_symbol.rs new file mode 100644 index 0000000000..c668bc5ba5 --- /dev/null +++ b/crates/compiler/specialize_types/src/foreign_symbol.rs @@ -0,0 +1,37 @@ +use roc_module::ident::ForeignSymbol; +use soa::Index; + +#[derive(Debug, Default)] +pub struct ForeignSymbols { + inner: Vec, +} + +impl ForeignSymbols { + pub fn get(&mut self, id: ForeignSymbolId) -> &ForeignSymbol { + // Safety: we only ever get indices that correspond to actual Vec entries + unsafe { self.inner.get_unchecked(id.inner.index()) } + } + + pub fn new() -> Self { + Self { + inner: Default::default(), + } + } +} + +impl ForeignSymbols { + pub fn push(&mut self, entry: ForeignSymbol) -> ForeignSymbolId { + let id = self.inner.len(); + + self.inner.push(entry); + + ForeignSymbolId { + inner: Index::new(id as u32), + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ForeignSymbolId { + inner: Index, +} diff --git a/crates/compiler/specialize_types/src/lib.rs b/crates/compiler/specialize_types/src/lib.rs new file mode 100644 index 0000000000..66a104de33 --- /dev/null +++ b/crates/compiler/specialize_types/src/lib.rs @@ -0,0 +1,24 @@ +// TODO [mono2]: re-enable when ready +#![allow(dead_code)] +#![allow(clippy::too_many_arguments)] + +mod debug_info; +mod foreign_symbol; +mod mono_expr; +mod mono_ir; +mod mono_module; +mod mono_num; +mod mono_struct; +mod mono_type; +// mod specialize_expr; +mod specialize_type; + +pub use debug_info::DebugInfo; +pub use foreign_symbol::{ForeignSymbolId, ForeignSymbols}; +pub use mono_expr::Env; +pub use mono_ir::{MonoExpr, MonoExprId, MonoExprs}; +pub use mono_module::{InternedStrId, Interns}; +pub use mono_num::Number; +pub use mono_struct::MonoFieldId; +pub use mono_type::{MonoType, MonoTypeId, MonoTypes}; +pub use specialize_type::{MonoTypeCache, Problem, RecordFieldIds, TupleElemIds}; diff --git a/crates/compiler/specialize_types/src/mono_expr.rs b/crates/compiler/specialize_types/src/mono_expr.rs new file mode 100644 index 0000000000..c566ccbd1e --- /dev/null +++ b/crates/compiler/specialize_types/src/mono_expr.rs @@ -0,0 +1,502 @@ +use crate::{ + mono_ir::{MonoExpr, MonoExprId, MonoExprs}, + mono_module::Interns, + mono_num::Number, + mono_type::{MonoType, MonoTypes, Primitive}, + specialize_type::{MonoTypeCache, Problem, RecordFieldIds, TupleElemIds}, + DebugInfo, MonoTypeId, +}; +use bumpalo::{collections::Vec, Bump}; +use roc_can::expr::{Expr, IntValue}; +use roc_collections::{Push, VecMap}; +use roc_module::symbol::ModuleId; +use roc_region::all::Region; +use roc_solve::module::Solved; +use roc_types::subs::{Content, Subs, Variable}; +use soa::NonEmptySlice; + +/// Function bodies that have already been specialized. +pub struct MonoFnCache { + inner: VecMap<(ModuleId, Variable, MonoTypeId), MonoExprId>, +} + +impl MonoFnCache { + pub fn monomorphize_fn<'a, F: 'a + FnOnce(ModuleId, Variable) -> &'a Expr>( + &mut self, + // Sometimes we need to create specializations of functions that are defined in other modules. + module_id: ModuleId, + // The function Variable stored in the original function's canonical Expr. We use this as a way to + // uniquely identify the function expr within its module, since each fn Expr gets its own unique var. + // Doing it with Variable instead of IdentId lets us cache specializations of anonymous functions too. + fn_var: Variable, + // Given a ModuleId and Variable (to uniquely identify the canonical fn Expr within its module), + // get the canonical Expr of the function itself. We need this to create a specialization of it. + // TODO [mono2] + _get_fn_expr: F, + // This tells us which specialization of the function we want. + mono_type_id: MonoTypeId, + ) -> MonoExprId { + *self + .inner + .get_or_insert((module_id, fn_var, mono_type_id), || { + todo!("TODO lower the fn_expr using Env etc. (May need to add args to this method, not sure.)"); + }) + } +} + +pub struct Env<'a, 'c, 'd, 'i, 's, 't, P> { + arena: &'a Bump, + subs: &'s mut Subs, + types_cache: &'c mut MonoTypeCache, + mono_types: &'t mut MonoTypes, + mono_exprs: &'t mut MonoExprs, + record_field_ids: RecordFieldIds, + tuple_elem_ids: TupleElemIds, + debug_info: &'d mut Option, + string_interns: &'i mut Interns<'a>, + problems: P, +} + +impl<'a, 'c, 'd, 'i, 's, 't, P: Push> Env<'a, 'c, 'd, 'i, 's, 't, P> { + pub fn new( + arena: &'a Bump, + subs: &'s mut Solved, + types_cache: &'c mut MonoTypeCache, + mono_types: &'t mut MonoTypes, + mono_exprs: &'t mut MonoExprs, + record_field_ids: RecordFieldIds, + tuple_elem_ids: TupleElemIds, + string_interns: &'i mut Interns<'a>, + debug_info: &'d mut Option, + problems: P, + ) -> Self { + Env { + arena, + subs: subs.inner_mut(), + types_cache, + mono_types, + mono_exprs, + record_field_ids, + tuple_elem_ids, + string_interns, + debug_info, + problems, + } + } + + pub fn to_mono_expr(&mut self, can_expr: &Expr) -> Option { + let problems = &mut self.problems; + let mono_types = &mut self.mono_types; + let mut mono_from_var = |var| { + self.types_cache.monomorphize_var( + self.arena, + self.subs, + mono_types, + &mut self.record_field_ids, + &mut self.tuple_elem_ids, + problems, + self.debug_info, + var, + ) + }; + + macro_rules! compiler_bug { + ($problem:expr) => {{ + problems.push($problem); + Some(MonoExpr::CompilerBug($problem)) + }}; + } + + match can_expr { + Expr::Float(var, _precision_var, _str, val, _bound) => { + match self.subs.get_content_without_compacting(*var) { + Content::FlexVar(_) => { + // Plain decimal number literals like `4.2` can still have an unbound var. + Some(MonoExpr::Number(Number::Dec(*val))) + } + _ => match mono_from_var(*var) { + Some(mono_id) => match mono_types.get(mono_id) { + MonoType::Primitive(primitive) => { + Some(to_frac(*primitive, *val, problems)) + } + other => { + compiler_bug!(Problem::NumSpecializedToWrongType(Some(*other))) + } + }, + None => { + compiler_bug!(Problem::NumSpecializedToWrongType(None)) + } + }, + } + } + Expr::Num(var, _, int_value, _) | Expr::Int(var, _, _, int_value, _) => { + // Number literals and int literals both specify integer numbers, so to_num() can work on both. + match mono_from_var(*var) { + Some(mono_id) => match mono_types.get(mono_id) { + MonoType::Primitive(primitive) => { + Some(to_num(*primitive, *int_value, problems)) + } + other => compiler_bug!(Problem::NumSpecializedToWrongType(Some(*other))), + }, + None => compiler_bug!(Problem::NumSpecializedToWrongType(None)), + } + } + Expr::SingleQuote(var, _, char, _) => match mono_from_var(*var) { + // Single-quote characters monomorphize to an integer. + // TODO if we store these using the same representation as other ints (e.g. Expr::Int, + // or keeping a separate value but storing an IntValue instead of a char), then + // even though we verify them differently, we can combine this branch with Num and Int. + Some(mono_id) => match mono_types.get(mono_id) { + MonoType::Primitive(primitive) => { + Some(char_to_int(*primitive, *char, problems)) + } + other => compiler_bug!(Problem::CharSpecializedToWrongType(Some(*other))), + }, + None => compiler_bug!(Problem::CharSpecializedToWrongType(None)), + }, + Expr::Str(contents) => Some(MonoExpr::Str(self.string_interns.get_id( + self.arena, + // TODO should be able to remove this alloc_str() once canonical Expr stores an arena-allocated string. + self.arena.alloc_str(contents), + ))), + Expr::EmptyRecord => { + // Empty records are zero-sized and should be discarded. + None + } + Expr::Record { + record_var: _, + fields, + } => { + // TODO [mono2]: store debuginfo for the record type, including ideally type alias and/or opaque type names. Do this before early-returning for single-field records. + + // Check for records with 0-1 fields before sorting or reserving a slice of IDs (which might be unnecessary). + // We'll check again after discarding zero-sized fields, because we might end up with 0 or 1 fields remaining. + if fields.len() <= 1 { + return fields + .into_iter() + .next() + .and_then(|(_, field)| self.to_mono_expr(&field.loc_expr.value)); + } + + // Sort the fields alphabetically by name. + let mut fields = Vec::from_iter_in(fields, self.arena); + + fields.sort_by(|(name1, _), (name2, _)| name1.cmp(name2)); + + // We want to end up with a Slice of these, so accumulate a buffer of them + // and then add them to MonoExprs all at once, so they're added as one contiguous slice + // regardless of what else got added to MonoExprs as we were monomorphizing them. + let mut buf: Vec<(MonoExpr, Region)> = + Vec::with_capacity_in(fields.len(), self.arena); + + buf.extend( + // flat_map these so we discard all the fields that monomorphized to None + fields.into_iter().flat_map(|(_name, field)| { + self.to_mono_expr(&field.loc_expr.value) + .map(|mono_expr| (mono_expr, field.loc_expr.region)) + }), + ); + + // If we ended up with exactly 1 field, return it unwrapped. + if buf.len() == 1 { + return buf.pop().map(|(expr, _region)| expr); + } + + NonEmptySlice::from_slice(self.mono_exprs.extend(buf.iter().copied())) + .map(MonoExpr::Struct) + } + // Expr::Call((fn_var, fn_expr, capture_var, ret_var), args, called_via) => { + // let opt_ret_type = mono_from_var(*var); + + // if opt_ret_type.is_none() { + // let fn_type = match self.subs.get_content_without_compacting(fn_var) { + // Content::Structure(FlatType::Func(arg_vars, closure_var, ret_var)) => { + // let todo = (); // TODO make is_effectful actually use the function's effectfulness! + // let is_effectful = false; + + // // Calls to pure functions that return zero-sized types should be discarded. + // if !is_effectful { + // return None; + // } + + // // Use the Content we already have to directly monomorphize the function, rather than + // // calling monomorphize_var and having it redo the Subs lookup and conditionals we just did. + // self.types_cache.monomorphize_fn( + // self.subs, + // self.mono_types, + // &mut self.record_field_ids, + // &mut self.tuple_elem_ids, + // &mut self.problems, + // self.debug_info, + // *arg_vars, + // *ret_var, + // )? + // } + // _ => { + // // This function didn't have a function type. Compiler bug! + // return Some(MonoExpr::CompilerBug(Problem::FnDidNotHaveFnType)); + // } + // }; + + // let todo = (); // TODO this is where we need to specialize, which means...duplicating the fn expr body maybe? and caching it under the mono type? + // let fn_expr = self.to_mono_expr(can_expr, stmts)?; + // let args = todo!(); // TODO compute the args. This is tricky because of preallocated slices! + // let capture_type = mono_from_var(*capture_var); + + // let todo = (); // How do we pre-reserve the statements? Is that possible? It does seem necessary...might not be possible though. Maybe we just need to make Vec rather than Slice on these. + + // // We aren't returning anything, and this is an effectful function, so just push a statement to call it and move on. + // stmts.push(self.mono_stmts.add(MonoStmt::CallVoid { + // fn_type, + // fn_expr, + // args, + // capture_type, + // })); + + // None + // } else { + // let fn_type = mono_from_var(*fn_var)?; + // let todo = (); // TODO this is where we need to specialize, which means...duplicating the fn expr body maybe? and caching it under the mono type? + // let fn_expr = self.to_mono_expr(can_expr, stmts)?; + // let args = todo!(); // TODO compute the args. This is tricky because of preallocated slices! + // let capture_type = mono_from_var(*capture_var); + + // Some(MonoExpr::Call { + // fn_type, + // fn_expr, + // args, + // capture_type, + // }) + // } + // } + // Expr::Var(symbol, var) => Some(MonoExpr::Lookup(*symbol, mono_from_var(*var)?)), + // Expr::LetNonRec(def, loc) => { + // let expr = self.to_mono_expr(def.loc_expr.value, stmts)?; + // let todo = (); // TODO if this is an underscore pattern and we're doing a fn call, convert it to Stmt::CallVoid + // let pattern = self.to_mono_pattern(def.loc_pattern.value); + + // // TODO do we need to use any of these other fields? e.g. for the types? + // // pub struct Def { + // // pub loc_pattern: Loc, + // // pub loc_expr: Loc, + // // pub expr_var: Variable, + // // pub pattern_vars: SendMap, + // // pub annotation: Option, + // // } + + // todo!("split up the pattern into various Assign statements."); + // } + // Expr::LetRec(vec, loc, illegal_cycle_mark) => todo!(), + _ => todo!(), + // Expr::List { + // elem_var, + // loc_elems, + // } => todo!(), + // Expr::IngestedFile(path_buf, arc, variable) => todo!(), + // Expr::ParamsVar { + // symbol, + // var, + // params_symbol, + // params_var, + // } => todo!(), + // Expr::AbilityMember(symbol, specialization_id, variable) => todo!(), + // Expr::When { + // loc_cond, + // cond_var, + // expr_var, + // region, + // branches, + // branches_cond_var, + // exhaustive, + // } => todo!(), + // Expr::If { + // cond_var, + // branch_var, + // branches, + // final_else, + // } => todo!(), + // Expr::Call(_, vec, called_via) => todo!(), + // Expr::RunLowLevel { op, args, ret_var } => todo!(), + // Expr::ForeignCall { + // foreign_symbol, + // args, + // ret_var, + // } => todo!(), + // Expr::Closure(closure_data) => todo!(), + // Expr::Tuple { tuple_var, elems } => todo!(), + // Expr::ImportParams(module_id, region, _) => todo!(), + // Expr::Crash { msg, ret_var } => todo!(), + // Expr::RecordAccess { + // record_var, + // ext_var, + // field_var, + // loc_expr, + // field, + // } => todo!(), + // Expr::RecordAccessor(struct_accessor_data) => todo!(), + // Expr::TupleAccess { + // tuple_var, + // ext_var, + // elem_var, + // loc_expr, + // index, + // } => todo!(), + // Expr::RecordUpdate { + // record_var, + // ext_var, + // symbol, + // updates, + // } => todo!(), + // Expr::Tag { + // tag_union_var, + // ext_var, + // name, + // arguments, + // } => todo!(), + // Expr::ZeroArgumentTag { + // closure_name, + // variant_var, + // ext_var, + // name, + // } => todo!(), + // Expr::OpaqueRef { + // opaque_var, + // name, + // argument, + // specialized_def_type, + // type_arguments, + // lambda_set_variables, + // } => todo!(), + // Expr::OpaqueWrapFunction(opaque_wrap_function_data) => todo!(), + // Expr::Expect { + // loc_condition, + // loc_continuation, + // lookups_in_cond, + // } => todo!(), + // Expr::ExpectFx { + // loc_condition, + // loc_continuation, + // lookups_in_cond, + // } => todo!(), + // Expr::Dbg { + // source_location, + // source, + // loc_message, + // loc_continuation, + // variable, + // symbol, + // } => todo!(), + // Expr::TypedHole(variable) => todo!(), + // Expr::RuntimeError(_runtime_error) => { + // todo!("generate a MonoExpr::Crash based on the runtime error"); + // } + } + } +} + +/// Convert a number literal (e.g. `42`) or integer literal (e.g. `0x42`) to a monomorphized type. +/// Nums are allowed to convert to either an integer or a fraction. Integer literals should have +/// given a compile-time error if they ended up unifying to a fractional type, but we can +/// gracefully allow them to compile to that type anyway. +fn to_num(primitive: Primitive, val: IntValue, problems: &mut impl Push) -> MonoExpr { + match primitive { + // These are ordered roughly by most to least common integer types + Primitive::U8 => MonoExpr::Number(Number::U8(val.as_i128() as u8)), + Primitive::I8 => MonoExpr::Number(Number::I8(val.as_i128() as i8)), + Primitive::U16 => MonoExpr::Number(Number::U16(val.as_i128() as u16)), + Primitive::I16 => MonoExpr::Number(Number::I16(val.as_i128() as i16)), + Primitive::U32 => MonoExpr::Number(Number::U32(val.as_i128() as u32)), + Primitive::I32 => MonoExpr::Number(Number::I32(val.as_i128() as i32)), + Primitive::U64 => MonoExpr::Number(Number::U64(val.as_i128() as u64)), + Primitive::I64 => MonoExpr::Number(Number::I64(val.as_i128() as i64)), + Primitive::F32 => MonoExpr::Number(Number::F32(val.as_i128() as f32)), + Primitive::F64 => MonoExpr::Number(Number::F64(val.as_i128() as f64)), + Primitive::Dec => MonoExpr::Number(Number::Dec(match val { + IntValue::I128(bytes) => i128::from_ne_bytes(bytes) as f64, + IntValue::U128(bytes) => u128::from_ne_bytes(bytes) as f64, + })), + Primitive::U128 => MonoExpr::Number(Number::U128(val.as_u128())), + Primitive::I128 => MonoExpr::Number(Number::I128(val.as_i128())), + Primitive::Str | Primitive::Crash => { + let problem = Problem::NumSpecializedToWrongType(Some(MonoType::Primitive(primitive))); + problems.push(problem); + MonoExpr::CompilerBug(problem) + } + } +} + +/// Convert a fractional literal (e.g. `0.5`) to a monomorphized type. +/// If somehow its type was not a fractional type, that's a compiler bug! +fn to_frac(primitive: Primitive, val: f64, problems: &mut impl Push) -> MonoExpr { + match primitive { + // These are ordered roughly by most to least common fractional types + Primitive::F32 => MonoExpr::Number(Number::F32(val as f32)), + Primitive::F64 => MonoExpr::Number(Number::F64(val)), + Primitive::Dec => MonoExpr::Number(Number::Dec(val)), + Primitive::U8 + | Primitive::I8 + | Primitive::U16 + | Primitive::I16 + | Primitive::U32 + | Primitive::I32 + | Primitive::U64 + | Primitive::I64 + | Primitive::U128 + | Primitive::I128 + | Primitive::Str + | Primitive::Crash => { + let problem = Problem::NumSpecializedToWrongType(Some(MonoType::Primitive(primitive))); + problems.push(problem); + MonoExpr::CompilerBug(problem) + } + } +} + +/// Convert a single-quote character (e.g. `'r'`) to a monomorphized type. +/// If somehow its type was not an integer type, that's a compiler bug! +fn char_to_int(primitive: Primitive, ch: char, problems: &mut impl Push) -> MonoExpr { + match primitive { + // These are ordered roughly by most to least common character types + Primitive::U8 => MonoExpr::Number(Number::U8(ch as u8)), + Primitive::U64 => MonoExpr::Number(Number::U64(ch as u64)), + Primitive::U16 => MonoExpr::Number(Number::U16(ch as u16)), + Primitive::U32 => MonoExpr::Number(Number::U32(ch as u32)), + Primitive::U128 => MonoExpr::Number(Number::U128(ch as u128)), + Primitive::I64 => MonoExpr::Number(Number::I64(ch as i64)), + Primitive::I32 => MonoExpr::Number(Number::I32(ch as i32)), + Primitive::I128 => MonoExpr::Number(Number::I128(ch as i128)), + Primitive::I16 => MonoExpr::Number(Number::I16(ch as i16)), + Primitive::I8 => MonoExpr::Number(Number::I8(ch as i8)), + Primitive::Str | Primitive::Dec | Primitive::F32 | Primitive::F64 | Primitive::Crash => { + let problem = Problem::CharSpecializedToWrongType(Some(MonoType::Primitive(primitive))); + problems.push(problem); + MonoExpr::CompilerBug(problem) + } + } +} + +// /// Convert a fraction literal (e.g. `4.2`) to a monomorphized type. +// /// If somehow its type was not a fraction type, that's a compiler bug! +// fn to_frac( +// number: mono_type::Number, +// val: FracValue, +// problems: &mut impl Push, +// ) -> MonoExpr { +// match number { +// mono_type::Number::Dec => Number::Dec(val.to_dec()), +// mono_type::Number::F32 => Number::F32(val.to_f32()), +// mono_type::Number::F64 => Number::F64(val.to_f64()), +// mono_type::Number::U8 +// | mono_type::Number::I8 +// | mono_type::Number::U16 +// | mono_type::Number::I16 +// | mono_type::Number::U32 +// | mono_type::Number::I32 +// | mono_type::Number::U64 +// | mono_type::Number::I64 +// | mono_type::Number::U128 +// | mono_type::Number::I128 => { +// // TODO push problem of frac monomorphizing to int, return Malformed expr +// } +// } +// } diff --git a/crates/compiler/specialize_types/src/mono_ir.rs b/crates/compiler/specialize_types/src/mono_ir.rs new file mode 100644 index 0000000000..249da15a2f --- /dev/null +++ b/crates/compiler/specialize_types/src/mono_ir.rs @@ -0,0 +1,372 @@ +use crate::{ + foreign_symbol::ForeignSymbolId, mono_module::InternedStrId, mono_num::Number, + mono_struct::MonoFieldId, mono_type::MonoTypeId, specialize_type::Problem, +}; +use roc_can::expr::Recursive; +use roc_module::low_level::LowLevel; +use roc_module::symbol::Symbol; +use roc_region::all::Region; +use soa::{Index, NonEmptySlice, Slice, Slice2, Slice3}; +use std::iter; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct MonoPatternId { + inner: u32, +} + +pub type IdentId = Symbol; // TODO make this an Index into an array local to this module + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Def { + pub pattern: MonoPatternId, + /// Named variables in the pattern, e.g. `a` in `Ok a ->` + pub pattern_vars: Slice2, + pub expr: MonoExprId, + pub expr_type: MonoTypeId, +} + +#[derive(Debug, Default)] +pub struct MonoExprs { + // TODO convert to Vec2 + exprs: Vec, + regions: Vec, +} + +impl MonoExprs { + pub fn new() -> Self { + Self { + exprs: Vec::new(), + regions: Vec::new(), + } + } + + pub fn add(&mut self, expr: MonoExpr, region: Region) -> MonoExprId { + let index = self.exprs.len() as u32; + self.exprs.push(expr); + self.regions.push(region); + + MonoExprId { + inner: Index::new(index), + } + } + + pub fn get_expr(&self, id: MonoExprId) -> &MonoExpr { + debug_assert!( + self.exprs.get(id.inner.index()).is_some(), + "A MonoExprId was not found in MonoExprs. This should never happen!" + ); + + // Safety: we should only ever hand out MonoExprIds that are valid indices into here. + unsafe { self.exprs.get_unchecked(id.inner.index()) } + } + + pub fn get_region(&self, id: MonoExprId) -> Region { + debug_assert!( + self.regions.get(id.inner.index()).is_some(), + "A MonoExprId was not found in MonoExprs. This should never happen!" + ); + + // Safety: we should only ever hand out MonoExprIds that are valid indices into here. + unsafe { *self.regions.get_unchecked(id.inner.index()) } + } + + pub fn reserve_id(&mut self) -> MonoExprId { + let answer = MonoExprId { + inner: Index::new(self.exprs.len() as u32), + }; + + // These should all be overwritten; if they aren't, that's a problem! + self.exprs + .push(MonoExpr::CompilerBug(Problem::UninitializedReservedExpr)); + self.regions.push(Region::zero()); + + answer + } + + pub fn reserve_ids(&mut self, len: u16) -> Slice { + let answer = Slice::new(self.exprs.len() as u32, len); + + // These should all be overwritten; if they aren't, that's a problem! + self.exprs.extend( + iter::repeat(MonoExpr::CompilerBug(Problem::UninitializedReservedExpr)) + .take(len as usize), + ); + self.regions + .extend(iter::repeat(Region::zero()).take(len as usize)); + + answer + } + + pub(crate) fn insert(&mut self, id: MonoExprId, mono_expr: MonoExpr, region: Region) { + debug_assert!( + self.exprs.get(id.inner.index()).is_some(), + "A MonoExprId was not found in MonoExprs. This should never happen!" + ); + + debug_assert!( + self.regions.get(id.inner.index()).is_some(), + "A MonoExprId was not found in MonoExprs. This should never happen!" + ); + + let index = id.inner.index(); + + // Safety: we should only ever hand out MonoExprIds that are valid indices into here. + unsafe { + *self.exprs.get_unchecked_mut(index) = mono_expr; + *self.regions.get_unchecked_mut(index) = region; + } + } + + pub fn iter_slice(&self, exprs: Slice) -> impl Iterator { + exprs.indices().map(|index| { + debug_assert!( + self.exprs.get(index).is_some(), + "A Slice index was not found in MonoExprs. This should never happen!" + ); + + // Safety: we should only ever hand out MonoExprId slices that are valid indices into here. + unsafe { self.exprs.get_unchecked(index) } + }) + } + + pub fn extend( + &mut self, + exprs: impl Iterator + Clone, + ) -> Slice { + let start = self.exprs.len(); + + self.exprs.extend(exprs.clone().map(|(expr, _region)| expr)); + self.regions.extend(exprs.map(|(_expr, region)| region)); + + let len = self.exprs.len() - start; + + Slice::new(start as u32, len as u16) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MonoExprId { + inner: Index, +} + +impl MonoExprId { + pub(crate) unsafe fn new_unchecked(inner: Index) -> Self { + Self { inner } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MonoStmtId { + inner: Index, +} + +impl MonoStmtId { + pub(crate) unsafe fn new_unchecked(inner: Index) -> Self { + Self { inner } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum MonoStmt { + /// Assign to a variable. + Assign(IdentId, MonoExprId), + AssignRec(IdentId, MonoExprId), + + /// Introduce a variable, e.g. `var foo_` (we'll MonoStmt::Assign to it later.) + Declare(IdentId), + + /// The `return` statement + Return(MonoExprId), + + /// The "crash" keyword. Importantly, during code gen we must mark this as "nothing happens after this" + Crash { + msg: MonoExprId, + /// The type of the `crash` expression (which will have unified to whatever's around it) + expr_type: MonoTypeId, + }, + + Expect { + condition: MonoExprId, + /// If the expectation fails, we print the values of all the named variables + /// in the final expr. These are those values. + lookups_in_cond: Slice2, + }, + + Dbg { + source_location: InternedStrId, + source: InternedStrId, + expr: MonoExprId, + expr_type: MonoTypeId, + name: IdentId, + }, + + // Call a function that has no return value (or which we are discarding due to an underscore pattern). + CallVoid { + fn_type: MonoTypeId, + fn_expr: MonoExprId, + args: Slice2, + /// This is the type of the closure based only on canonical IR info, + /// not considering what other closures might later influence it. + /// Lambda set specialization may change this type later! + capture_type: MonoTypeId, + }, + + // Branching + When { + /// The actual condition of the when expression. + cond: MonoExprId, + cond_type: MonoTypeId, + /// Type of each branch (and therefore the type of the entire `when` expression) + branch_type: MonoTypeId, + /// Note: if the branches weren't exhaustive, we will have already generated a default + /// branch which crashes if it's reached. (The compiler will have reported an error already; + /// this is for if you want to run anyway.) + branches: NonEmptySlice, + }, + If { + /// Type of each branch (and therefore the type of the entire `if` expression) + branch_type: MonoTypeId, + branches: Slice<(MonoStmtId, MonoStmtId)>, + final_else: Option, + }, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum MonoExpr { + Str(InternedStrId), + Number(Number), + List { + elem_type: MonoTypeId, + elems: Slice, + }, + Lookup(IdentId, MonoTypeId), + + /// Like Lookup, but from a module with params + ParameterizedLookup { + name: IdentId, + lookup_type: MonoTypeId, + params_name: IdentId, + params_type: MonoTypeId, + }, + + /// This is *only* for calling functions, not for tag application. + /// The Tag variant contains any applied values inside it. + Call { + fn_type: MonoTypeId, + fn_expr: MonoExprId, + args: Slice2, + /// This is the type of the closure based only on canonical IR info, + /// not considering what other closures might later influence it. + /// Lambda set specialization may change this type later! + capture_type: MonoTypeId, + }, + RunLowLevel { + op: LowLevel, + args: Slice<(MonoTypeId, MonoExprId)>, + ret_type: MonoTypeId, + }, + ForeignCall { + foreign_symbol: ForeignSymbolId, + args: Slice<(MonoTypeId, MonoExprId)>, + ret_type: MonoTypeId, + }, + + Lambda { + fn_type: MonoTypeId, + arguments: Slice<(MonoTypeId, MonoPatternId)>, + body: MonoExprId, + captured_symbols: Slice<(IdentId, MonoTypeId)>, + recursive: Recursive, + }, + + /// A record literal or a tuple literal. + /// These have already been sorted alphabetically. + Struct(NonEmptySlice), + + /// Look up exactly one field on a record, tuple, or tag payload. + /// At this point we've already unified those concepts and have + /// converted (for example) record field names to indices, and have + /// also dropped all fields that have no runtime representation (e.g. empty records). + /// + /// In a later compilation phase, these indices will be re-sorted + /// by alignment and converted to byte offsets, but we in this + /// phase we aren't concerned with alignment or sizes, just indices. + StructAccess { + record_expr: MonoExprId, + record_type: MonoTypeId, + field_type: MonoTypeId, + field_id: MonoFieldId, + }, + + RecordUpdate { + record_type: MonoTypeId, + record_name: IdentId, + updates: Slice2, + }, + + /// Same as BigTag but with u8 discriminant instead of u16 + SmallTag { + discriminant: u8, + tag_union_type: MonoTypeId, + args: Slice2, + }, + + /// Same as SmallTag but with u16 discriminant instead of u8 + BigTag { + discriminant: u16, + tag_union_type: MonoTypeId, + args: Slice2, + }, + + Block { + stmts: Slice, + final_expr: MonoExprId, + }, + + CompilerBug(Problem), +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct WhenBranch { + pub patterns: Slice, + pub body: Slice, + pub guard: Option, +} + +#[derive(Clone, Copy, Debug)] +pub enum MonoPattern { + Identifier(IdentId), + As(MonoPatternId, IdentId), + StrLiteral(InternedStrId), + NumberLiteral(Number), + AppliedTag { + tag_union_type: MonoTypeId, + tag_name: IdentId, + args: Slice, + }, + StructDestructure { + struct_type: MonoTypeId, + destructs: Slice3, + }, + List { + elem_type: MonoTypeId, + patterns: Slice, + + /// Where a rest pattern splits patterns before and after it, if it does at all. + /// If present, patterns at index >= the rest index appear after the rest pattern. + /// For example: + /// [ .., A, B ] -> patterns = [A, B], rest = 0 + /// [ A, .., B ] -> patterns = [A, B], rest = 1 + /// [ A, B, .. ] -> patterns = [A, B], rest = 2 + /// Optionally, the rest pattern can be named - e.g. `[ A, B, ..others ]` + opt_rest: Option<(u16, Option)>, + }, + Underscore, +} + +#[derive(Clone, Copy, Debug)] +pub enum DestructType { + Required, + Optional(MonoTypeId, MonoExprId), + Guard(MonoTypeId, MonoPatternId), +} diff --git a/crates/compiler/specialize_types/src/mono_module.rs b/crates/compiler/specialize_types/src/mono_module.rs new file mode 100644 index 0000000000..b6ed6ee0ae --- /dev/null +++ b/crates/compiler/specialize_types/src/mono_module.rs @@ -0,0 +1,78 @@ +use bumpalo::Bump; +use roc_solve::module::Solved; +use roc_types::subs::Subs; + +use crate::{foreign_symbol::ForeignSymbols, mono_type::MonoTypes, DebugInfo}; + +pub struct MonoModule { + mono_types: MonoTypes, + foreign_symbols: ForeignSymbols, + // TODO [mono2]: interner type + interned_strings: Vec, + debug_info: DebugInfo, +} + +impl MonoModule { + pub fn from_typed_can_module(_subs: &Solved) -> Self { + Self { + mono_types: MonoTypes::new(), + foreign_symbols: ForeignSymbols::new(), + interned_strings: Vec::new(), + debug_info: DebugInfo, + } + } +} + +/// TODO move this to its own crate +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct InternedStrId(u32); + +/// TODO move this to its own crate +#[derive(Debug, Default)] +pub struct Interns<'a> { + interned: Vec<&'a str>, +} + +impl<'a> Interns<'a> { + pub fn new() -> Self { + Self { + interned: Vec::new(), + } + } + + pub fn get_str(&self, _arena: &'a Bump, id: InternedStrId) -> &'a str { + let index = id.0 as usize; + + #[cfg(debug_assertions)] + { + assert!(self.interned.get(index).is_some(), "Got an InternedStringId ({index}) that was outside the bounds of the backing array. This should never happen!"); + } + + // Safety: We should only ever give out InternedStrId values that are in this range. + unsafe { self.interned.get_unchecked(index) } + } + + pub fn get_id(&mut self, _arena: &'a Bump, string: &'a str) -> InternedStrId { + match self + .interned + .iter() + .position(|&interned| interned == string) + { + Some(index) => InternedStrId(index as u32), + None => { + let answer = InternedStrId(self.interned.len() as u32); + + self.interned.push(string); + + answer + } + } + } + + pub fn try_get_id(&self, _arena: &'a Bump, string: &'a str) -> Option { + self.interned + .iter() + .position(|&interned| interned == string) + .map(|index| InternedStrId(index as u32)) + } +} diff --git a/crates/compiler/specialize_types/src/mono_num.rs b/crates/compiler/specialize_types/src/mono_num.rs new file mode 100644 index 0000000000..5456e7ba54 --- /dev/null +++ b/crates/compiler/specialize_types/src/mono_num.rs @@ -0,0 +1,16 @@ +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Number { + I8(i8), + U8(u8), + I16(i16), + U16(u16), + I32(i32), + U32(u32), + I64(i64), + U64(u64), + I128(i128), + U128(u128), + F32(f32), + F64(f64), + Dec(f64), +} diff --git a/crates/compiler/specialize_types/src/mono_struct.rs b/crates/compiler/specialize_types/src/mono_struct.rs new file mode 100644 index 0000000000..71032aba4d --- /dev/null +++ b/crates/compiler/specialize_types/src/mono_struct.rs @@ -0,0 +1,13 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MonoFieldId { + inner: u16, +} +impl MonoFieldId { + pub fn new(index: u16) -> Self { + Self { inner: index } + } + + pub fn as_index(self) -> usize { + self.inner as usize + } +} diff --git a/crates/compiler/specialize_types/src/mono_type.rs b/crates/compiler/specialize_types/src/mono_type.rs new file mode 100644 index 0000000000..4a6986f424 --- /dev/null +++ b/crates/compiler/specialize_types/src/mono_type.rs @@ -0,0 +1,296 @@ +use core::num::NonZeroU16; +use soa::{Index, NonEmptySlice, Slice}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MonoTypeId { + inner: Index, +} + +impl MonoTypeId { + pub const CRASH: Self = Self { + inner: Index::new(0), + }; + + pub const STR: Self = Self { + inner: Index::new(1), + }; + + pub const U8: Self = Self { + inner: Index::new(2), + }; + + pub const I8: Self = Self { + inner: Index::new(3), + }; + + pub const U16: Self = Self { + inner: Index::new(4), + }; + + pub const I16: Self = Self { + inner: Index::new(5), + }; + + pub const U32: Self = Self { + inner: Index::new(6), + }; + + pub const I32: Self = Self { + inner: Index::new(7), + }; + pub const U64: Self = Self { + inner: Index::new(8), + }; + + pub const I64: Self = Self { + inner: Index::new(9), + }; + + pub const U128: Self = Self { + inner: Index::new(10), + }; + + pub const I128: Self = Self { + inner: Index::new(11), + }; + + pub const F32: Self = Self { + inner: Index::new(12), + }; + + pub const F64: Self = Self { + inner: Index::new(13), + }; + + pub const DEC: Self = Self { + inner: Index::new(14), + }; + + pub const DEFAULT_INT: Self = Self::I64; // TODO change this to I128 + pub const DEFAULT_FRAC: Self = Self::DEC; + + fn new(inner: Index) -> Self { + Self { inner } + } +} + +#[derive(Debug, Default)] +pub struct MonoTypes { + entries: Vec, + ids: Vec, + slices: Vec<(NonZeroU16, MonoTypeId)>, // TODO make this a Vec2 +} + +impl MonoTypes { + pub fn new() -> Self { + Self { + entries: vec![ + MonoType::Primitive(Primitive::Crash), + MonoType::Primitive(Primitive::Str), + MonoType::Primitive(Primitive::U8), + MonoType::Primitive(Primitive::I8), + MonoType::Primitive(Primitive::U16), + MonoType::Primitive(Primitive::I16), + MonoType::Primitive(Primitive::U32), + MonoType::Primitive(Primitive::I32), + MonoType::Primitive(Primitive::U64), + MonoType::Primitive(Primitive::I64), + MonoType::Primitive(Primitive::U128), + MonoType::Primitive(Primitive::I128), + MonoType::Primitive(Primitive::F32), + MonoType::Primitive(Primitive::F64), + MonoType::Primitive(Primitive::Dec), + ], + ids: Vec::new(), + slices: Vec::new(), + } + } + pub fn get(&self, id: MonoTypeId) -> &MonoType { + // Future strategy: + // - Look at the three high bits to figure out which of the 8 MonoTypes we're dealing with + // - The non-parameterized builtins have 000 as their high bits, and the whole MonoTypeId can be cast to a Primitive. + // - The parameterized builtins don't need to store a length, just an index. We store that index inline. + // - The non-builtins all store a length and an index. We store the index inline, and the length out of band. + // - Dictionaries store their second param adjacent to the first. + // - This means we use 2 bits for discriminant and another 2 bits for which parameterized type it is + // - This means we get 29-bit indices, so a maximum of ~500M MonoTypes per module. Should be plenty. + // - In the future, we can promote common collection types (e.g. List Str, List U8) to Primitives. + + let opt = self.entries.get(id.inner.index()); + + debug_assert!(opt.is_some(), "A MonoTypeId corresponded to an index that wasn't in MonoTypes. This should never happen!"); + + unsafe { opt.unwrap_unchecked() } + } + + pub(crate) fn add_primitive(&mut self, _primitive: Primitive) -> MonoTypeId { + todo!("if it's one of the hardcoded ones, find the associated MonoTypeId; otherwise, store it etc."); + } + + pub(crate) fn add_function( + &mut self, + ret: Option, + args: impl IntoIterator, + ) -> MonoTypeId { + let mono_type = match ret { + Some(ret) => { + let ret_then_args = { + let start = self.ids.len(); + self.ids.push(ret); + self.ids.extend(args); + // Safety: we definitely have at least 2 elements in here, even if the iterator is empty. + let length = + unsafe { NonZeroU16::new_unchecked((self.ids.len() - start) as u16) }; + + NonEmptySlice::new(start as u32, length) + }; + + MonoType::Func { ret_then_args } + } + None => { + let args = { + let start = self.ids.len(); + self.ids.extend(args); + let length = (self.ids.len() - start) as u16; + + Slice::new(start as u32, length) + }; + + MonoType::VoidFunc { args } + } + }; + + let index = self.entries.len(); + self.entries.push(mono_type); + MonoTypeId::new(Index::new(index as u32)) + } + + /// This should only be given iterators with at least 2 elements in them. + /// We receive the fields in sorted order (e.g. by record field name or by tuple index). + /// A later compiler phase will stable-sort them by alignment (we don't deal with alignment here), + /// and that phase will also sort its DebugInfo struct fields in the same way. + pub(crate) unsafe fn add_struct_unchecked( + &mut self, + fields: impl Iterator, + ) -> MonoTypeId { + let start = self.ids.len(); + self.extend_ids(fields); + let len = self.ids.len() - start; + let non_empty_slice = + // Safety: This definitely has at least 2 elements in it, because we just added them. + unsafe { NonEmptySlice::new_unchecked(start as u32, len as u16)}; + let index = self.entries.len(); + self.entries.push(MonoType::Struct(non_empty_slice)); + MonoTypeId::new(Index::new(index as u32)) + } + + /// We receive the payloads in sorted order (sorted by tag name). + pub(crate) fn add_tag_union( + &mut self, + first_payload: MonoTypeId, + second_payload: MonoTypeId, + other_payloads: impl Iterator, + ) -> MonoTypeId { + let start = self.ids.len(); + self.ids.push(first_payload); + self.ids.push(second_payload); + self.extend_ids(other_payloads); + let len = self.ids.len() - start; + let non_empty_slice = + // Safety: This definiely has at least 2 elements in it, because we just added them. + unsafe { NonEmptySlice::new_unchecked(start as u32, len as u16)}; + let index = self.entries.len(); + self.entries.push(MonoType::Struct(non_empty_slice)); + MonoTypeId::new(Index::new(index as u32)) + } + + fn extend_ids(&mut self, iter: impl Iterator) -> Slice { + let start = self.ids.len(); + self.ids.extend(iter); + let length = self.ids.len() - start; + + Slice::new(start as u32, length as u16) + } + + pub(crate) fn add(&mut self, entry: MonoType) -> MonoTypeId { + let id = Index::new(self.entries.len() as u32); + + self.entries.push(entry); + + MonoTypeId { inner: id } + } +} + +// TODO: we can make all of this take up a minimal amount of memory as follows: +// 1. Arrange it so that for each MonoType variant we need at most one length and one start index. +// 2. Store all MonoType discriminants in one array (there are only 5 of them, so u3 is plenty; +// if we discard record field names, can unify record and tuple and use u2 for the 4 variants) +// 3. Store all the MonoType variant slice lengths in a separate array (u8 should be plenty) +// 4. Store all the MonoType start indices in a separate array (u32 should be plenty) + +/// Primitive means "Builtin type that has no type parameters" (so, numbers, Str, and Unit) +/// +/// In the future, we may promote common builtin types to Primitives, e.g. List U8, List Str, etc. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Primitive { + Crash, + Str, + U8, + I8, + U16, + I16, + U32, + I32, + U64, + I64, + U128, + I128, + F32, + F64, + Dec, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MonoType { + Primitive(Primitive), + Box(MonoTypeId), + List(MonoTypeId), + /// Records, tuples, and tag union payloads all end up here. (Empty ones are handled separate.) + /// + /// Slice of field types, ordered alphabetically by field name (or by tuple elem index). + /// The strings for the field names (or tuple indices) are stored out of band in DebugInfo, + /// which references this MonoTypeId. A later compiler phase will sort these by alignment + /// (this phase is not aware of alignment), and will sort the DebugInfo structs accordingly. + Struct(NonEmptySlice), + + /// Slice of payloads, where each payload is a struct or Unit. (Empty tag unions become Unit.) + /// + /// These have already been sorted alphabetically by tag name, and the tag name strings + /// have already been recorded out of band in DebugInfo. + TagUnion(NonEmptySlice), + + /// A function that has a return value and 0 or more arguments. + /// To avoid wasting memory, we store the return value first in the nonempty slice, + /// and then the arguments after it. + Func { + ret_then_args: NonEmptySlice, + }, + + /// A function that does not have a return value (e.g. the return value was {}, which + /// got eliminated), and which has 0 or more arguments. + /// This has to be its own variant because the other function variant uses one slice to + /// store the return value followed by the arguments. Without this separate variant, + /// the other one couldn't distinguish between a function with a return value and 0 arguments, + /// and a function with 1 argument but no return value. + VoidFunc { + args: Slice, + }, + // This last slot is tentatively reserved for Dict, because in the past we've discussed wanting to + // implement Dict in Zig (for performance) instead of on top of List, like it is as of this writing. + // + // Assuming we do that, Set would still be implemented as a Dict with a unit type for the value, + // so we would only need one variant for both. + // + // The second type param would be stored adjacent to the first, so we only need to store one index. + // Dict(MonoTypeId), +} diff --git a/crates/compiler/specialize_types/src/specialize_type.rs b/crates/compiler/specialize_types/src/specialize_type.rs new file mode 100644 index 0000000000..794bdf269e --- /dev/null +++ b/crates/compiler/specialize_types/src/specialize_type.rs @@ -0,0 +1,650 @@ +/// Given a Subs that's been populated from type inference, and a Variable, +/// ensure that Variable is monomorphic by going through and creating +/// specializations of that type wherever necessary. +/// +/// This only operates at the type level. It does not create new function implementations (for example). +use crate::{ + debug_info::DebugInfo, + mono_type::{MonoTypeId, MonoTypes}, + MonoFieldId, MonoType, +}; +use bumpalo::{collections::Vec, Bump}; +use roc_collections::{Push, VecMap}; +use roc_module::{ident::Lowercase, symbol::Symbol}; +use roc_solve::module::Solved; +use roc_types::{ + subs::{Content, FlatType, Subs, SubsSlice, Variable}, + types::AliasKind, +}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Problem { + // Compiler bugs; these should never happen! + TagUnionExtWasNotTagUnion, + RecordExtWasNotRecord, + TupleExtWasNotTuple, + /// This can be either an integer specializing to a fractional number type (or vice versa), + /// or the type parameter specializing to a non-numeric type (e.g. Num Str), which should + /// have been caught during type-checking and changed to an Error type. + NumSpecializedToWrongType( + Option, // `None` means it specialized to Unit + ), + CharSpecializedToWrongType( + Option, // `None` means it specialized to Unit + ), + BadNumTypeParam, + UninitializedReservedExpr, + FnDidNotHaveFnType, +} + +/// For MonoTypes that are records, store their field indices. +pub type RecordFieldIds = VecMap>; + +/// For MonoTypes that are tuples, store their element indices. +/// (These are not necessarily the same as their position in the monomorphized tuple, +/// because we may have deleted some zero-sized types in the middle - yet expressions +/// will still refer to e.g. `tuple.1`, so we still need to know which element `.1` +/// referred to originally before we deleted things. +pub type TupleElemIds = VecMap>; + +/// Variables that have already been monomorphized. +pub struct MonoTypeCache { + inner: VecMap, +} + +impl MonoTypeCache { + pub fn from_solved_subs(subs: &Solved) -> Self { + Self { + inner: VecMap::with_capacity(subs.inner().len()), + } + } + + /// Returns None if it monomorphizes to a type that should be eliminated + /// (e.g. a zero-sized type like empty record, empty tuple, a record of just those, etc.) + pub fn monomorphize_var( + &mut self, + arena: &Bump, + subs: &Subs, + mono_types: &mut MonoTypes, + field_indices: &mut RecordFieldIds, + elem_indices: &mut TupleElemIds, + problems: &mut impl Push, + debug_info: &mut Option, + var: Variable, + ) -> Option { + let mut env = Env { + arena, + cache: self, + mono_types, + field_ids: field_indices, + elem_ids: elem_indices, + problems, + debug_info, + }; + + env.lower_var(subs, var) + } +} + +struct Env<'a, 'c, 'd, 'e, 'f, 'm, 'p, P> { + arena: &'a Bump, + cache: &'c mut MonoTypeCache, + mono_types: &'m mut MonoTypes, + field_ids: &'f mut RecordFieldIds, + elem_ids: &'e mut TupleElemIds, + problems: &'p mut P, + debug_info: &'d mut Option, +} + +impl<'a, 'c, 'd, 'e, 'f, 'm, 'p, P: Push> Env<'a, 'c, 'd, 'e, 'f, 'm, 'p, P> { + fn lower_builtin( + &mut self, + subs: &Subs, + symbol: Symbol, + args: SubsSlice, + ) -> MonoTypeId { + if symbol == Symbol::NUM_NUM { + number_args_to_mono_id(args, subs, self.problems) + } else if symbol == Symbol::NUM_FLOATINGPOINT { + num_num_args_to_mono_id(symbol, args, subs, self.problems) + } else if symbol == Symbol::LIST_LIST { + todo!(); + // let mut new_args = args + // .into_iter() + // .flat_map(|var_index| self.lower_var( subs, subs[var_index])); + + // let arg = new_args.next(); + } else { + todo!("implement lower_builtin for symbol {symbol:?} - or, if all the builtins are already in here, report a compiler bug instead of panicking like this."); + } + } + + /// Exposed separately because sometimes we already looked up the Content and know it's a function, + /// and want to continue from there without redoing the lookup. + pub fn monomorphize_fn( + &mut self, + subs: &Subs, + arg_vars: SubsSlice, + ret_var: Variable, + // TODO [mono2] + _fx_var: Variable, + ) -> MonoTypeId { + let func = self.lower_var(subs, ret_var); + let mut mono_args = Vec::with_capacity_in(arg_vars.len(), self.arena); + + mono_args.extend( + arg_vars + .into_iter() + .flat_map(|var_index| self.lower_var(subs, subs[var_index])), + ); + + // TODO [mono2] populate debuginfo as appropriate + + self.mono_types.add_function(func, mono_args) + } + + fn lower_var(&mut self, subs: &Subs, var: Variable) -> Option { + let root_var = subs.get_root_key_without_compacting(var); + + // TODO: we could replace this cache by having Subs store a Content::Monomorphic(MonoTypeId) + // and then overwrite it rather than having a separate cache. That memory is already in cache + // for sure, and the lookups should be faster because they're O(1) but don't require hashing. + // Kinda creates a cyclic dep though. + if let Some(mono_id) = self.cache.inner.get(&root_var) { + return Some(*mono_id); + } + + // Convert the Content to a MonoType, often by passing an iterator. None of these iterators introduce allocations. + let mono_id = match dbg!(*subs.get_content_without_compacting(root_var)) { + Content::Structure(flat_type) => match flat_type { + FlatType::Apply(symbol, args) => { + if symbol.is_builtin() { + self.lower_builtin(subs, symbol, args) + } else { + todo!("handle non-builtin Apply"); + } + } + FlatType::Func(args, _capture, ret, fx) => { + self.monomorphize_fn(subs, args, ret, fx) + } + _ => { + todo!(); + } /* + FlatType::Record(fields, ext) => { + let mut labeled_mono_ids = lower_record(env, fields, ext); + + // Handle the special cases of 0 fields and 1 field. + match labeled_mono_ids.first() { + Some((label, first_field_id)) => { + if labeled_mono_ids.len() == 1 { + // If we ended up with a single field, return it unwrapped. + let todo = (); // TODO populate debuginfo using the label (if it's Some, meaning we want it) + let todo = (); // To preserve debuginfo, we need to actually clone this mono_id and not just return the same one. + return Some(*first_field_id); + } + } + None => { + // If we ended up with an empty record, + // after removing other empty things, return None. + return None; + } + } + + // Now we know we have at least 2 fields, so sort them by field name. + // This can be unstable sort because all field names are known to be unique, + // so sorting unstable won't be observable (and is faster than stable). + labeled_mono_ids.sort_unstable_by(|(label1, _), (label2, _)| label1.cmp(label2)); + + let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it) + + // Safety: we already verified that this has at least 2 elements, and + // we would have early returned before this point if we had fewer than 2. + let mono_id = unsafe { + mono_types.add_struct_unchecked(labeled_mono_ids.iter().map(|(_label, mono_id)| *mono_id)) + }; + + let labeled_indices = VecMap::from_iter(labeled_mono_ids.into_iter().enumerate().map(|(index, (label, _mono_id))| (label, MonoFieldId::new(index as u16)))); + + self.field_ids.insert(mono_id, labeled_indices); + + Some(mono_id) + } + FlatType::Tuple(elems, ext) => { + let indexed_mono_ids = lower_tuple(env, elems, ext); + + // This can be unstable sort because all indices are known to be unique, + // so sorting unstable won't be observable (and is faster than stable). + indexed_mono_ids.sort_unstable_by(|(index1, _), (index2, _)| index1.cmp(index2)); + + let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it) + mono_types.add_struct(indexed_mono_ids.iter().map(|(_, mono_id)| *mono_id)) + } + FlatType::TagUnion(tags, ext) => { + let tagged_payload_ids = lower_tag_union(env, tags, ext); + + // This can be unstable sort because all tag names are known to be unique, + // so sorting unstable won't be observable (and is faster than stable). + tagged_payload_ids.sort_unstable_by(|(tag1, _), (tag2, _)| tag1.cmp(tag2)); + + let todo = (); // TODO populate debuginfo (if it's Some, meaning we want it) + mono_types.add_tag_union(tagged_payload_ids.iter().map(|(_, mono_id)| *mono_id)) + } + FlatType::FunctionOrTagUnion(tag_names, _symbols, ext) => { + // If this is still a FunctionOrTagUnion, turn it into a TagUnion. + + // First, resolve the ext var. + let mut tags = resolve_tag_ext(subs, problems, UnionTags::default(), *ext); + + // Now lower all the tags we gathered from the ext var. + // (Do this in a separate pass to avoid borrow errors on Subs.) + lower_vars(tags.iter_mut().flat_map(|(_, vars)| vars.iter_mut()), cache, subs, problems); + + // Then, add the tag names with no payloads. (There are no variables to lower here.) + for index in tag_names.into_iter() { + tags.push(((subs[index]).clone(), Vec::new())); + } + + Content::Structure(FlatType::TagUnion( + UnionTags::insert_into_subs(subs, tags), + TagExt::Any(Variable::EMPTY_TAG_UNION), + )) + } + FlatType::RecursiveTagUnion(rec, tags, ext) => { + let mut tags = resolve_tag_ext(subs, problems, *tags, *ext); + + // Now lower all the tags we gathered. Do this in a separate pass to avoid borrow errors on Subs. + lower_vars(tags.iter_mut().flat_map(|(_, vars)| vars.iter_mut()), cache, subs, problems); + + Content::Structure(FlatType::RecursiveTagUnion( + lower_var(cache, subs, problems, *rec), + UnionTags::insert_into_subs(subs, tags), + TagExt::Any(Variable::EMPTY_TAG_UNION), + )) + } + FlatType::EmptyRecord| + FlatType::EmptyTuple | + FlatType::EmptyTagUnion => None, + }, + Content::Error => Content::Error, + */ + }, + Content::RangedNumber(range) => { + use roc_types::num::NumericRange::*; + + match range { + IntAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width), + IntAtLeastEitherSign(int_lit_width) => { + int_lit_width_to_mono_type_id(int_lit_width) + } + NumAtLeastSigned(int_lit_width) => int_lit_width_to_mono_type_id(int_lit_width), + NumAtLeastEitherSign(int_lit_width) => { + int_lit_width_to_mono_type_id(int_lit_width) + } + } + } + Content::Alias(symbol, args, real, kind) => { + match kind { + AliasKind::Opaque if symbol.is_builtin() => { + let args_slice = + SubsSlice::new(args.variables_start, args.type_variables_len); + self.lower_builtin(subs, symbol, args_slice) + } + _ => { + // TODO [mono2] record in debuginfo the alias name for whatever we're lowering. + self.lower_var(subs, real)? + } + } + } + Content::FlexVar(_) + | Content::RigidVar(_) + | Content::FlexAbleVar(_, _) + | Content::RigidAbleVar(_, _) => { + // The only way we should reach this branch is in something like a `crash`. + MonoTypeId::CRASH + } + Content::ErasedLambda | Content::LambdaSet(_) => { + unreachable!( + "This new monomorphization implementation must not do anything with lambda sets, because they'll be handled later!" + ); + } + content => { + todo!("specialize this Content: {content:?}"); + } + }; + + // This var is now known to be monomorphic, so we don't repeat this work again later. + // (We don't insert entries for Unit values.) + self.cache.inner.insert(root_var, mono_id); + + Some(mono_id) + } +} + +fn int_lit_width_to_mono_type_id(int_lit_width: roc_can::num::IntLitWidth) -> MonoTypeId { + use roc_can::num::IntLitWidth; + + match int_lit_width { + IntLitWidth::U8 => MonoTypeId::U8, + IntLitWidth::U16 => MonoTypeId::U16, + IntLitWidth::U32 => MonoTypeId::U32, + IntLitWidth::U64 => MonoTypeId::U64, + IntLitWidth::U128 => MonoTypeId::U128, + IntLitWidth::I8 => MonoTypeId::I8, + IntLitWidth::I16 => MonoTypeId::I16, + IntLitWidth::I32 => MonoTypeId::I32, + IntLitWidth::I64 => MonoTypeId::I64, + IntLitWidth::I128 => MonoTypeId::I128, + IntLitWidth::F32 => MonoTypeId::F32, + IntLitWidth::F64 => MonoTypeId::F64, + IntLitWidth::Dec => MonoTypeId::DEC, + } +} + +/// This works on the arg(s) to a Num.Num +fn num_num_args_to_mono_id( + outer_symbol: Symbol, + args: SubsSlice, + subs: &Subs, + problems: &mut impl Push, +) -> MonoTypeId { + match args.into_iter().next() { + Some(arg_index) if args.len() == 1 => { + let mut content = subs.get_content_without_compacting(subs[arg_index]); + + // Unroll aliases in this loop, as many aliases as we encounter. + loop { + match content { + Content::Structure(flat_type) => { + if let FlatType::Apply(inner_symbol, args) = flat_type { + let inner_symbol = *inner_symbol; + + if args.is_empty() { + if outer_symbol == Symbol::NUM_INTEGER { + if inner_symbol == Symbol::NUM_UNSIGNED8 { + return MonoTypeId::U8; + } else if inner_symbol == Symbol::NUM_SIGNED8 { + return MonoTypeId::I8; + } else if inner_symbol == Symbol::NUM_UNSIGNED16 { + return MonoTypeId::U16; + } else if inner_symbol == Symbol::NUM_SIGNED16 { + return MonoTypeId::I16; + } else if inner_symbol == Symbol::NUM_UNSIGNED32 { + return MonoTypeId::U32; + } else if inner_symbol == Symbol::NUM_SIGNED32 { + return MonoTypeId::I32; + } else if inner_symbol == Symbol::NUM_UNSIGNED64 { + return MonoTypeId::U64; + } else if inner_symbol == Symbol::NUM_SIGNED64 { + return MonoTypeId::I64; + } else if inner_symbol == Symbol::NUM_UNSIGNED128 { + return MonoTypeId::U128; + } else if inner_symbol == Symbol::NUM_SIGNED128 { + return MonoTypeId::I128; + } + } else if outer_symbol == Symbol::NUM_FLOATINGPOINT { + if inner_symbol == Symbol::NUM_BINARY32 { + return MonoTypeId::F32; + } else if inner_symbol == Symbol::NUM_BINARY64 { + return MonoTypeId::F64; + } else if inner_symbol == Symbol::NUM_DECIMAL { + return MonoTypeId::DEC; + } + } + } + } + } + Content::FlexVar(_) => { + if outer_symbol == Symbol::NUM_INTEGER { + // Int * + return MonoTypeId::DEFAULT_INT; + } else if outer_symbol == Symbol::NUM_FLOATINGPOINT { + // Frac * + return MonoTypeId::DEFAULT_FRAC; + } + } + Content::Alias(_symbol, _alias_variables, variable, alias_kind) => { + match alias_kind { + AliasKind::Structural => { + // Unwrap the alias and continue the loop. + // + // (Unlike in most aliases, here we don't care about the name + // for debug info purposes; all we care about is determining + // whether it's one of the builtin types.) + content = subs.get_content_without_compacting(*variable); + } + AliasKind::Opaque => { + // This should never happen (type-checking should have caught it), + // so if an opaque type made it here, it's definitely a compiler bug! + break; + } + } + } + Content::RangedNumber(_numeric_range) => todo!(), + _ => { + // This is an invalid number type, so break out of + // the alias-unrolling loop in order to return an error. + break; + } + } + } + } + _ => { + // This is an invalid number type, so fall through to the error case. + } + } + + // If we got here, it's because the Num type parameter(s) don't fit the form we expect. + // Specialize to a crash! + problems.push(Problem::BadNumTypeParam); + + MonoTypeId::CRASH +} + +fn number_args_to_mono_id( + args: SubsSlice, + subs: &Subs, + problems: &mut impl Push, +) -> MonoTypeId { + match args.into_iter().next() { + Some(arg_index) if args.len() == 1 => { + let mut content = subs.get_content_without_compacting(subs[arg_index]); + + // Unroll aliases in this loop, as many aliases as we encounter. + loop { + match content { + Content::Structure(flat_type) => { + if let FlatType::Apply(outer_symbol, args) = flat_type { + return num_num_args_to_mono_id(*outer_symbol, *args, subs, problems); + } else { + break; + } + } + Content::FlexVar(_) => { + // Num * + return MonoTypeId::DEFAULT_INT; + } + Content::Alias(_symbol, _alias_variables, variable, alias_kind) => { + match alias_kind { + AliasKind::Structural => { + // Unwrap the alias and continue the loop. + // + // (Unlike in most aliases, here we don't care about the name + // for debug info purposes; all we care about is determining + // whether it's one of the builtin types.) + content = subs.get_content_without_compacting(*variable); + } + AliasKind::Opaque => { + // This should never happen (type-checking should have caught it), + // so if an opaque type made it here, it's definitely a compiler bug! + break; + } + } + } + Content::RangedNumber(_numeric_range) => todo!(), + _ => { + // This is an invalid number type, so break out of + // the alias-unrolling loop in order to return an error. + break; + } + } + } + } + _ => { + // The Num type did not have exactly 1 type parameter; fall through to the error case. + } + } + + // If we got here, it's because the Num type parameter(s) don't fit the form we expect. + // Specialize to a crash! + problems.push(Problem::BadNumTypeParam); + + MonoTypeId::CRASH +} + +// fn resolve_tag_ext( +// subs: &mut Subs, +// mono_types: &mut MonoTypes, +// problems: &mut impl Push, +// mut tags: UnionTags, +// mut ext: TagExt, +// ) -> Vec<(TagName, Vec)> { +// let mut all_tags = Vec::new(); + +// // Collapse (recursively) all the tags in ext_var into a flat list of tags. +// loop { +// for (tag, vars) in tags.iter_from_subs(subs) { +// all_tags.push((tag.clone(), vars.to_vec())); +// } + +// match subs.get_content_without_compacting(ext.var()) { +// Content::Structure(FlatType::TagUnion(new_tags, new_ext)) => { +// // Update tags and ext and loop back again to process them. +// tags = *new_tags; +// ext = *new_ext; +// } +// Content::Structure(FlatType::FunctionOrTagUnion(tag_names, _symbols, new_ext)) => { +// for index in tag_names.into_iter() { +// all_tags.push((subs[index].clone(), Vec::new())); +// } +// ext = *new_ext; +// } +// Content::Structure(FlatType::EmptyTagUnion) => break, +// Content::FlexVar(_) | Content::FlexAbleVar(_, _) => break, +// Content::Alias(_, _, real, _) => { +// // Follow the alias and process it on the next iteration of the loop. +// ext = TagExt::Any(*real); + +// // We just processed these tags, so don't process them again! +// tags = UnionLabels::default(); +// } +// _ => { +// // This should never happen! If it does, record a Problem and break. +// problems.push(Problem::TagUnionExtWasNotTagUnion); + +// break; +// } +// } +// } + +// all_tags +// } + +// fn lower_record>( +// env: &mut Env<'_, '_, '_, '_, '_, '_, P>, +// subs: &Subs, +// mut fields: RecordFields, +// mut ext: Variable, +// ) -> Vec<(Lowercase, Option)> { +// let mut labeled_mono_ids = Vec::with_capacity(fields.len()); + +// // Collapse (recursively) all the fields in ext into a flat list of fields. +// loop { +// // Add all the current fields to the answer. +// labeled_mono_ids.extend( +// fields +// .sorted_iterator(subs, ext) +// .map(|(label, field)| (label, self.lower_var( subs, *field.as_inner()))), +// ); + +// // If the ext record is nonempty, set its fields to be the next ones we handle, and loop back. +// match subs.get_content_without_compacting(ext) { +// Content::Structure(FlatType::Record(new_fields, new_ext)) => { +// // Update fields and ext and loop back again to process them. +// fields = *new_fields; +// ext = *new_ext; +// } +// Content::Structure(FlatType::EmptyRecord) +// | Content::FlexVar(_) +// | Content::FlexAbleVar(_, _) => return labeled_mono_ids, +// Content::Alias(_, _, real, _) => { +// // Follow the alias and process it on the next iteration of the loop. +// ext = *real; + +// // We just processed these fields, so don't process them again! +// fields = RecordFields::empty(); +// } +// _ => { +// // This should never happen! If it does, record a Problem and early return. +// env.problems.push(Problem::RecordExtWasNotRecord); + +// return labeled_mono_ids; +// } +// } +// } +// } + +// fn resolve_tuple_ext( +// subs: &mut Subs, +// mono_types: &mut MonoTypes, +// problems: &mut impl Push, +// mut elems: TupleElems, +// mut ext: Variable, +// ) -> Vec<(usize, Variable)> { +// let mut all_elems = Vec::new(); + +// // Collapse (recursively) all the elements in ext into a flat list of elements. +// loop { +// for (idx, var_index) in elems.iter_all() { +// all_elems.push((idx.index as usize, subs[var_index])); +// } + +// match subs.get_content_without_compacting(ext) { +// Content::Structure(FlatType::Tuple(new_elems, new_ext)) => { +// // Update elems and ext and loop back again to process them. +// elems = *new_elems; +// ext = *new_ext; +// } +// Content::Structure(FlatType::EmptyTuple) => break, +// Content::FlexVar(_) | Content::FlexAbleVar(_, _) => break, +// Content::Alias(_, _, real, _) => { +// // Follow the alias and process it on the next iteration of the loop. +// ext = *real; + +// // We just processed these elements, so don't process them again! +// elems = TupleElems::empty(); +// } +// _ => { +// // This should never happen! If it does, record a Problem and break. +// problems.push(Problem::TupleExtWasNotTuple); + +// break; +// } +// } +// } + +// all_elems +// } + +// /// Lower the given vars in-place. +// fn lower_vars<'a>( +// vars: impl Iterator, +// cache: &mut MonoCache, +// subs: &mut Subs, +// mono_types: &mut MonoTypes, +// problems: &mut impl Push, +// ) { +// for var in vars { +// if let Some(var) = self.lower_var( *var) // hmm not sure if this is still a good idea as a helper function +// *var = ; +// } +// } diff --git a/crates/compiler/specialize_types/tests/helpers/mod.rs b/crates/compiler/specialize_types/tests/helpers/mod.rs new file mode 100644 index 0000000000..e65093ecd0 --- /dev/null +++ b/crates/compiler/specialize_types/tests/helpers/mod.rs @@ -0,0 +1,282 @@ +// TODO [mono2]: re-enable when ready +#![allow(dead_code)] + +use bumpalo::Bump; +use core::fmt::Write; +use roc_load::LoadedModule; +use roc_region::all::Region; +use roc_solve::FunctionKind; +use roc_specialize_types::{ + DebugInfo, Env, Interns, MonoExpr, MonoExprs, MonoTypeCache, MonoTypes, RecordFieldIds, + TupleElemIds, +}; +use test_compile::{trim_and_deindent, SpecializedExprOut}; +use test_solve_helpers::{format_problems, run_load_and_infer}; + +fn specialize_expr<'a>( + arena: &'a Bump, + src: &str, + string_interns: &mut Interns<'a>, +) -> SpecializedExprOut { + let ( + LoadedModule { + module_id: home, + mut declarations_by_id, + mut can_problems, + mut type_problems, + interns, + mut solved, + mut exposed_to_host, + abilities_store, + .. + }, + src, + ) = run_load_and_infer( + trim_and_deindent(arena, src), + [], + false, + FunctionKind::LambdaSet, + ) + .unwrap(); + + let mut can_problems = can_problems.remove(&home).unwrap_or_default(); + let type_problems = type_problems.remove(&home).unwrap_or_default(); + + // Disregard UnusedDef problems, because those are unavoidable when + // returning a function from the test expression. + can_problems.retain(|prob| { + !matches!( + prob, + roc_problem::can::Problem::UnusedDef(_, _) + | roc_problem::can::Problem::UnusedBranchDef(..) + ) + }); + + let (can_problems, type_problems) = + format_problems(&src, home, &interns, can_problems, type_problems); + + assert_eq!(can_problems, String::new()); + assert_eq!(type_problems, String::new()); + + exposed_to_host.retain(|s, _| !abilities_store.is_specialization_name(*s)); + + let mut problems = Vec::new(); + let mut debug_info: Option = None; + let mut types_cache = MonoTypeCache::from_solved_subs(&solved); + let mut mono_types = MonoTypes::new(); + let mut mono_exprs = MonoExprs::new(); + + let mut env = Env::new( + arena, + &mut solved, + &mut types_cache, + &mut mono_types, + &mut mono_exprs, + RecordFieldIds::default(), + TupleElemIds::default(), + string_interns, + &mut debug_info, + &mut problems, + ); + + let mut home_decls = declarations_by_id.remove(&home).unwrap(); + let main_expr = home_decls.expressions.pop().unwrap().value; + + // This should be our only expr + assert_eq!(0, home_decls.expressions.len()); + + let region = Region::zero(); + let mono_expr_id = env + .to_mono_expr(&main_expr) + .map(|mono_expr| mono_exprs.add(mono_expr, region)); + + SpecializedExprOut { + mono_expr_id, + problems, + mono_types, + mono_exprs, + region, + } +} + +#[track_caller] +pub fn expect_no_expr(input: impl AsRef) { + let arena = Bump::new(); + let mut interns = Interns::new(); + let out = specialize_expr(&arena, input.as_ref(), &mut interns); + let actual = out.mono_expr_id.map(|id| out.mono_exprs.get_expr(id)); + + assert_eq!(None, actual, "This input expr should have specialized to being dicarded as zero-sized, but it didn't: {:?}", input.as_ref()); +} + +#[track_caller] +pub fn expect_mono_expr(input: impl AsRef, mono_expr: MonoExpr) { + expect_mono_expr_with_interns(input, |_, _| mono_expr); +} + +#[track_caller] +pub fn expect_mono_expr_str(input: impl AsRef, expr_str: impl AsRef) { + expect_mono_expr_custom( + input, + |_, _, _| expr_str.as_ref().to_string(), + |arena, mono_exprs, interns, expr| { + dbg_mono_expr(arena, mono_exprs, interns, expr).to_string() + }, + ); +} + +fn dbg_mono_expr<'a>( + arena: &'a Bump, + mono_exprs: &MonoExprs, + interns: &Interns<'a>, + expr: &MonoExpr, +) -> &'a str { + let mut buf = bumpalo::collections::String::new_in(arena); + + dbg_mono_expr_help(arena, mono_exprs, interns, expr, &mut buf); + + buf.into_bump_str() +} + +fn dbg_mono_expr_help<'a>( + arena: &'a Bump, + mono_exprs: &MonoExprs, + interns: &Interns<'a>, + expr: &MonoExpr, + buf: &mut impl Write, +) { + match expr { + MonoExpr::Str(interned_str_id) => { + write!(buf, "Str({:?})", interns.get_str(arena, *interned_str_id)).unwrap(); + } + MonoExpr::Number(number) => { + write!(buf, "Number({:?})", number).unwrap(); + } + MonoExpr::Struct(field_exprs) => { + write!(buf, "Struct([").unwrap(); + + for (index, expr) in mono_exprs.iter_slice(field_exprs.as_slice()).enumerate() { + if index > 0 { + write!(buf, ", ").unwrap(); + } + + dbg_mono_expr_help(arena, mono_exprs, interns, expr, buf); + } + + write!(buf, "])").unwrap(); + } + // MonoExpr::List { elem_type, elems } => todo!(), + // MonoExpr::Lookup(symbol, mono_type_id) => todo!(), + // MonoExpr::ParameterizedLookup { + // name, + // lookup_type, + // params_name, + // params_type, + // } => todo!(), + // MonoExpr::When { + // cond, + // cond_type, + // branch_type, + // branches, + // } => todo!(), + // MonoExpr::If { + // branch_type, + // branches, + // final_else, + // } => todo!(), + // MonoExpr::LetRec { defs, ending_expr } => todo!(), + // MonoExpr::LetNonRec { def, ending_expr } => todo!(), + // MonoExpr::Call { + // fn_type, + // fn_expr, + // args, + // closure_type, + // } => todo!(), + // MonoExpr::RunLowLevel { op, args, ret_type } => todo!(), + // MonoExpr::ForeignCall { + // foreign_symbol, + // args, + // ret_type, + // } => todo!(), + // MonoExpr::Lambda { + // fn_type, + // arguments, + // body, + // captured_symbols, + // recursive, + // } => todo!(), + // MonoExpr::Crash { msg, expr_type } => todo!(), + // MonoExpr::StructAccess { + // record_expr, + // record_type, + // field_type, + // field_id, + // } => todo!(), + // MonoExpr::RecordUpdate { + // record_type, + // record_name, + // updates, + // } => todo!(), + // MonoExpr::SmallTag { + // discriminant, + // tag_union_type, + // args, + // } => todo!(), + // MonoExpr::BigTag { + // discriminant, + // tag_union_type, + // args, + // } => todo!(), + // MonoExpr::Expect { + // condition, + // continuation, + // lookups_in_cond, + // } => todo!(), + // MonoExpr::Dbg { + // source_location, + // source, + // msg, + // continuation, + // expr_type, + // name, + // } => todo!(), + MonoExpr::CompilerBug(problem) => { + write!(buf, "CompilerBug({:?})", problem).unwrap(); + } + other => { + todo!("Implement dbg_mono_expr for {:?}", other) + } + } +} + +#[track_caller] +pub fn expect_mono_expr_with_interns( + input: impl AsRef, + from_interns: impl for<'a> Fn(&'a Bump, &Interns<'a>) -> MonoExpr, +) { + expect_mono_expr_custom( + input, + |arena, _exprs, interns| from_interns(arena, interns), + |_, _, _, expr| *expr, + ); +} + +#[track_caller] +pub fn expect_mono_expr_custom( + input: impl AsRef, + to_expected: impl for<'a> Fn(&'a Bump, &MonoExprs, &Interns<'a>) -> T, + to_actual: impl for<'a> Fn(&'a Bump, &MonoExprs, &Interns<'a>, &MonoExpr) -> T, +) { + let arena = Bump::new(); + let mut string_interns = Interns::new(); + let out = specialize_expr(&arena, input.as_ref(), &mut string_interns); + let mono_expr_id = out + .mono_expr_id + .expect("This input expr should not have been discarded as zero-sized, but it was discarded: {input:?}"); + + let actual_expr = out.mono_exprs.get_expr(mono_expr_id); // Must run first, to populate string interns! + let actual = to_actual(&arena, &out.mono_exprs, &string_interns, actual_expr); + let expected = to_expected(&arena, &out.mono_exprs, &string_interns); + + assert_eq!(expected, actual); +} diff --git a/crates/compiler/specialize_types/tests/specialize_primitives.rs b/crates/compiler/specialize_types/tests/specialize_primitives.rs new file mode 100644 index 0000000000..f24054c9c7 --- /dev/null +++ b/crates/compiler/specialize_types/tests/specialize_primitives.rs @@ -0,0 +1,173 @@ +#[macro_use] +extern crate pretty_assertions; + +#[cfg(test)] +mod helpers; + +#[cfg(test)] +mod specialize_primitives { + use roc_specialize_types::{MonoExpr, Number}; + + use super::helpers::{expect_mono_expr, expect_mono_expr_with_interns}; + + #[test] + fn string_literal() { + let string = "foo"; + let expected = format!("\"{string}\""); + expect_mono_expr_with_interns(expected, |arena, interns| { + MonoExpr::Str(interns.try_get_id(arena, string).unwrap()) + }); + } + + #[test] + fn unbound_zero() { + let expected = 0; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I8(expected)), + ); + } + + #[test] + fn unbound_negative_i8() { + let expected = -42; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I8(expected)), + ); + } + + #[test] + fn unbound_positive_i8() { + let expected = 42; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I8(expected)), + ); + } + + #[test] + fn unbound_u8() { + let expected = 128; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U8(expected)), + ); + } + + #[test] + fn unbound_negative_i16() { + let expected = -5_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I16(expected)), + ); + } + + #[test] + fn unbound_positive_i16() { + let expected = 5_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I16(expected)), + ); + } + + #[test] + fn unbound_u16() { + let expected = 65_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U16(expected)), + ); + } + #[test] + fn unbound_negative_i32() { + let expected = -2_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I32(expected)), + ); + } + + #[test] + fn unbound_positive_i32() { + let expected = 2_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I32(expected)), + ); + } + + #[test] + fn unbound_u32() { + let expected = 4_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U32(expected)), + ); + } + + #[test] + fn unbound_negative_i64() { + let expected = -9_000_000_000_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I64(expected)), + ); + } + + #[test] + fn unbound_positive_i64() { + let expected = 9_000_000_000_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I64(expected)), + ); + } + + #[test] + fn unbound_u64() { + let expected = 18_000_000_000_000_000_000; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U64(expected)), + ); + } + + #[test] + fn unbound_negative_i128() { + let expected = -170_141_183_460_469_231_731_687_303_715_884_105_728; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I128(expected)), + ); + } + + #[test] + fn unbound_positive_i128() { + let expected = 170_141_183_460_469_231_731_687_303_715_884_105_727; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::I128(expected)), + ); + } + + #[test] + fn unbound_u128() { + let expected = 340_282_366_920_938_463_463_374_607_431_768_211_455; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::U128(expected)), + ); + } + + #[test] + fn unbound_f64() { + let expected = std::f64::consts::PI; + expect_mono_expr( + format!("{expected}"), + MonoExpr::Number(Number::Dec(expected)), + ); + } +} diff --git a/crates/compiler/specialize_types/tests/specialize_structs.rs b/crates/compiler/specialize_types/tests/specialize_structs.rs new file mode 100644 index 0000000000..f0d2e7e042 --- /dev/null +++ b/crates/compiler/specialize_types/tests/specialize_structs.rs @@ -0,0 +1,54 @@ +#[macro_use] +extern crate pretty_assertions; + +#[cfg(test)] +mod helpers; + +#[cfg(test)] +mod specialize_structs { + use roc_specialize_types::MonoExpr; + + use crate::helpers::expect_mono_expr_str; + + use super::helpers::{expect_mono_expr_with_interns, expect_no_expr}; + + #[test] + fn empty_record() { + expect_no_expr("{}"); + } + + #[test] + fn one_field_with_empty_record() { + expect_no_expr("{ discardedField: {} }"); + } + + #[test] + fn one_field_record_string_literal() { + let string = "foo"; + let expected = format!("{{ discardedField: \"{string}\" }}"); + expect_mono_expr_with_interns(expected, |arena, interns| { + MonoExpr::Str(interns.try_get_id(arena, string).unwrap()) + }); + } + + #[test] + fn one_field_after_dropping_zero_sized() { + let string = "foo"; + let expected = + format!("{{ discarded: {{}}, discardedToo: \"{string}\", alsoDiscarded: {{}} }}"); + expect_mono_expr_with_interns(expected, |arena, interns| { + MonoExpr::Str(interns.try_get_id(arena, string).unwrap()) + }); + } + + #[test] + fn two_fields() { + let one = 42; + let two = 50; + + expect_mono_expr_str( + format!("{{ one: {one}, two: {two} }}"), + format!("Struct([Number(I8({one})), Number(I8({two}))])"), + ); + } +} diff --git a/crates/compiler/test_derive/src/util.rs b/crates/compiler/test_derive/src/util.rs index 811e6b117e..d5897ec660 100644 --- a/crates/compiler/test_derive/src/util.rs +++ b/crates/compiler/test_derive/src/util.rs @@ -439,6 +439,7 @@ fn check_derived_typechecks_and_golden( derived_module: Default::default(), module_params: None, module_params_vars: imported_param_vars, + host_exposed_symbols: None, #[cfg(debug_assertions)] checkmate: None, diff --git a/crates/compiler/test_gen/src/gen_num.rs b/crates/compiler/test_gen/src/gen_num.rs index 388e402413..6ab46c0252 100644 --- a/crates/compiler/test_gen/src/gen_num.rs +++ b/crates/compiler/test_gen/src/gen_num.rs @@ -1607,7 +1607,7 @@ fn tail_call_elimination() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] -fn int_negate() { +fn num_negate() { assert_evals_to!("Num.neg 123i8", -123, i8); assert_evals_to!("Num.neg Num.maxI8", -i8::MAX, i8); assert_evals_to!("Num.neg (Num.minI8 + 1)", i8::MAX, i8); @@ -1623,6 +1623,26 @@ fn int_negate() { assert_evals_to!("Num.neg 123", -123, i64); assert_evals_to!("Num.neg Num.maxI64", -i64::MAX, i64); assert_evals_to!("Num.neg (Num.minI64 + 1)", i64::MAX, i64); + + assert_evals_to!("Num.neg 12.3f32", -12.3, f32); + assert_evals_to!("Num.neg 0.0f32", -0.0, f32); + assert_evals_to!("Num.neg Num.maxF32", -f32::MAX, f32); + assert_evals_to!("Num.neg Num.minF32", -f32::MIN, f32); + assert_evals_to!("Num.neg Num.infinityF32", -f32::INFINITY, f32); + // can't test equality for nan + assert_evals_to!("Num.isNaN (Num.neg Num.nanF32)", true, bool); + + assert_evals_to!("Num.neg 12.3f64", -12.3, f64); + assert_evals_to!("Num.neg 0.0f64", -0.0, f64); + assert_evals_to!("Num.neg Num.maxF64", -f64::MAX, f64); + assert_evals_to!("Num.neg Num.minF64", -f64::MIN, f64); + assert_evals_to!("Num.neg Num.infinityF64", -f64::INFINITY, f64); + // can't test equality for nan + assert_evals_to!("Num.isNaN (Num.neg Num.nanF64)", true, bool); + + assert_evals_to!("Num.neg 123dec", RocDec::from(-123), RocDec); + // 0 is signless, unlike f32/f64 + assert_evals_to!("Num.neg 0dec", RocDec::from(0), RocDec); } #[test] diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index 6e3913fff6..91ed28dcfc 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -542,6 +542,7 @@ pub fn try_run_lib_function( #[allow(dead_code)] // only used in tests +#[allow(dead_code)] pub(crate) fn llvm_evals_to( src: &str, expected: U, diff --git a/crates/compiler/test_syntax/fuzz/README.md b/crates/compiler/test_syntax/fuzz/README.md index ce3fd835d7..ff96def8b1 100644 --- a/crates/compiler/test_syntax/fuzz/README.md +++ b/crates/compiler/test_syntax/fuzz/README.md @@ -4,7 +4,7 @@ To setup fuzzing you will need to install cargo-fuzz and run with rust nightly: ```sh $ cargo install cargo-fuzz -$ cargo +nightly fuzz run -j -- -dict=../parse/fuzz/dict.txt +$ cargo +nightly fuzz run -j -- -dict=../dict.txt ``` The different targets can be found by running `cargo fuzz list`. diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/underscore_expr_in_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/malformed/underscore_expr_in_def.expr.result-ast index 43f45640ee..c292137433 100644 --- a/crates/compiler/test_syntax/tests/snapshots/malformed/underscore_expr_in_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/underscore_expr_in_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-3, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast index ede6aa85bb..d926638def 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-43, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast index 1cb962fc21..09ef23f942 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-52, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast index 017412f9d8..5dd22a2163 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-55, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast index 7e21cc2acb..25d88f1d00 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast @@ -10,12 +10,12 @@ SpaceAfter( @53-104, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast index 12f2249a2b..14c8962d0d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_closed_union.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-38, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast index 998b247a5c..d0e20356a4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_effectful_fn.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-89, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast index 3d78d0c00e..6258a99a03 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ann_open_union.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-39, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast index 30bd3a25b3..548b3a8cb6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-49, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast index a737af53a1..2cc93df84f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-46, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast index 6c548a4d9d..d0b6667d56 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-41, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast index c6dd36f721..d284b5583c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast @@ -9,10 +9,10 @@ SpaceBefore( @107-112, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast index 0ee368ce19..46c57cca11 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-164, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_annotation.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_annotation.expr.result-ast index 54b7042d72..75702a0339 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_annotation.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_annotation.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-3, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.result-ast index de7670cd3b..0af08c9c5a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-7, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_colon_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_colon_def.expr.result-ast index fe68d890c5..7bb65ad6d3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_colon_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_colon_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-5, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_equals_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_equals_def.expr.result-ast index 9aa645ec20..e31c6457c8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_equals_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comment_before_equals_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-5, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast index ec23824430..f8b2794f15 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast @@ -16,18 +16,18 @@ SpaceAfter( @75-101, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, - Slice { start: 2, length: 1 }, - Slice { start: 3, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, + Slice { start: 2, length: 1 }, + Slice { start: 3, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 3, length: 0 }, - Slice { start: 4, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 3, length: 0 }, + Slice { start: 4, length: 0 }, ], spaces: [ Newline, @@ -140,10 +140,10 @@ SpaceAfter( @81-93, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast index c298fa3337..b8b9e76ca5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast @@ -1,6 +1,6 @@ SpaceAfter( Apply( - @0-5 Dbg, + @0-3 Dbg, [ @4-5 Num( "1", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.formatted.roc index 44de35e8d2..9e53fc6a9a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.formatted.roc @@ -1,3 +1,3 @@ -dbg 1 == 1 +dbg (1 == 1) 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.result-ast index 895d9cf6e3..d6e409f5ed 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.result-ast @@ -1,20 +1,22 @@ SpaceBefore( SpaceAfter( DbgStmt( - @5-11 BinOps( - [ - ( - @5-6 Num( - "1", + @6-12 ParensAround( + BinOps( + [ + ( + @6-7 Num( + "1", + ), + @8-10 Equals, ), - @7-9 Equals, + ], + @11-12 Num( + "1", ), - ], - @10-11 Num( - "1", ), ), - @13-14 SpaceBefore( + @15-16 SpaceBefore( Num( "4", ), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.roc index fab415486d..a9eef8ef26 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt.expr.roc @@ -1,4 +1,4 @@ -dbg 1 == 1 +dbg (1 == 1) 4 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.formatted.roc index 92aa85cf3f..dd0ca03778 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.formatted.roc @@ -1,6 +1,8 @@ dbg - q - qt + ( + q + qt + ) g qt \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.result-ast index 12532d964a..677c819ca1 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.result-ast @@ -1,53 +1,38 @@ SpaceAfter( DbgStmt( - @5-10 SpaceBefore( - Defs( - Defs { - tags: [ - EitherIndex(2147483648), - ], - regions: [ - @5-6, - ], - space_before: [ - Slice { start: 0, length: 0 }, - ], - space_after: [ - Slice { start: 0, length: 0 }, - ], - spaces: [], - type_defs: [], - value_defs: [ - Stmt( - @5-6 Var { - module_name: "", - ident: "q", - }, - ), - ], - }, - @8-10 SpaceBefore( - Var { + @6-14 SpaceBefore( + ParensAround( + Apply( + @6-7 Var { module_name: "", - ident: "qt", + ident: "q", }, [ - Newline, + @12-14 SpaceBefore( + Var { + module_name: "", + ident: "qt", + }, + [ + Newline, + ], + ), ], + Space, ), ), [ Newline, ], ), - @11-16 SpaceBefore( + @16-21 SpaceBefore( Apply( - @11-12 Var { + @16-17 Var { module_name: "", ident: "g", }, [ - @14-16 SpaceBefore( + @19-21 SpaceBefore( Var { module_name: "", ident: "qt", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.roc index 7cf4569d26..406e17a371 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_stmt_two_exprs.expr.roc @@ -1,5 +1,5 @@ dbg - q - qt + (q + qt) g qt diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast index dc5947f6a5..d67a2bce27 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/def_bang.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast index 5d64a16465..e215ef27e2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast @@ -8,12 +8,12 @@ Defs { @176-266, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, @@ -38,12 +38,12 @@ Defs { @97-111, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, @@ -113,10 +113,10 @@ Defs { @215-224, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast index 55d2f2eece..50bf0bf832 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-36, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast index 7f377d15d7..8716e6de4a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast @@ -9,10 +9,10 @@ SpaceBefore( @48-53, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast index 6fe182b7bb..0f7df5fc3e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/effectful_closure_statements.expr.result-ast @@ -19,14 +19,14 @@ SpaceAfter( @61-154, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 3, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 3, length: 0 }, ], spaces: [ Newline, @@ -89,10 +89,10 @@ SpaceAfter( @86-119, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast index db5f8952bd..9947f3b3ac 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-13, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast index de5d80d2fe..8312e3ef28 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-644, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -31,14 +31,14 @@ Defs { @258-556, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, + Slice { start: 2, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast index 80966f1dfe..a130879059 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast @@ -12,14 +12,14 @@ SpaceAfter( @22-35, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, + Slice { start: 2, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast index 955f2619d5..c3ad86957f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast @@ -17,10 +17,10 @@ SpaceAfter( @16-21, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline_in_parens.expr.result-ast index c2b4a8250f..d59e99b4bb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline_in_parens.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-4, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast index 475051e8bc..54e53a7555 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/fn_with_record_arg.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-89, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index 6f68ac97a2..bba976aa01 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-32, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index 4ffd212055..c8e7b05d8d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-42, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast index 71d7b1e87d..66a2edd326 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-6, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast index b179c81e2c..c6d341c857 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-12, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast index 45619356f0..7bc25c6801 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast @@ -12,16 +12,16 @@ Defs { @93-146, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, - Slice { start: 2, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, + Slice { start: 2, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 3, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 3, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast index 91a6090607..8bcacd569f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast @@ -8,12 +8,12 @@ Defs { @24-50, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast index 201c30aa33..af18a838e6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast @@ -30,34 +30,34 @@ Defs { @710-718, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, - Slice { start: 4, length: 2 }, - Slice { start: 6, length: 2 }, - Slice { start: 8, length: 2 }, - Slice { start: 10, length: 2 }, - Slice { start: 12, length: 2 }, - Slice { start: 14, length: 2 }, - Slice { start: 16, length: 2 }, - Slice { start: 18, length: 2 }, - Slice { start: 20, length: 3 }, - Slice { start: 23, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, + Slice { start: 2, length: 2 }, + Slice { start: 4, length: 2 }, + Slice { start: 6, length: 2 }, + Slice { start: 8, length: 2 }, + Slice { start: 10, length: 2 }, + Slice { start: 12, length: 2 }, + Slice { start: 14, length: 2 }, + Slice { start: 16, length: 2 }, + Slice { start: 18, length: 2 }, + Slice { start: 20, length: 3 }, + Slice { start: 23, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 0 }, - Slice { start: 6, length: 0 }, - Slice { start: 8, length: 0 }, - Slice { start: 10, length: 0 }, - Slice { start: 12, length: 0 }, - Slice { start: 14, length: 0 }, - Slice { start: 16, length: 0 }, - Slice { start: 18, length: 0 }, - Slice { start: 20, length: 0 }, - Slice { start: 23, length: 0 }, - Slice { start: 25, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 0 }, + Slice { start: 6, length: 0 }, + Slice { start: 8, length: 0 }, + Slice { start: 10, length: 0 }, + Slice { start: 12, length: 0 }, + Slice { start: 14, length: 0 }, + Slice { start: 16, length: 0 }, + Slice { start: 18, length: 0 }, + Slice { start: 20, length: 0 }, + Slice { start: 23, length: 0 }, + Slice { start: 25, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast index e0341a157b..88a2f4e00e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast @@ -10,14 +10,14 @@ Defs { @76-99, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast index 79a680706b..ba685b3494 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast @@ -12,16 +12,16 @@ Defs { @97-157, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, - Slice { start: 2, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, + Slice { start: 2, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 3, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 3, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast index 2eeb237996..5a6ee4c3a6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-288, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -78,10 +78,10 @@ SpaceAfter( @123-192, ], space_before: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 1, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast index ca1c1ab5ef..024af56c6c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast @@ -8,12 +8,12 @@ Defs { @40-85, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast index 550cf67e37..d3302ee7a0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast @@ -10,12 +10,12 @@ SpaceAfter( @27-51, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast index fdbf17719c..ee3defb0f8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast index 2d9bc22969..e273ec4ea6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-27, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast index 3f26b5dc6e..54c53baf31 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-57, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast index 4481a36b0f..b161793b60 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-25, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast index 8cbf9fd03a..3bdfd3d1a6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast index 80c9380434..ff97e32cbe 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast @@ -9,10 +9,10 @@ SpaceBefore( @113-118, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast index a8583eeb5d..c42dc87e69 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-24, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -30,10 +30,10 @@ Defs { @11-17, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast index f73c014f1c..570b112baf 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-50, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast index 4e67398afb..b49f2a0d0f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-13, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast index f81cf1dea7..90dcb06779 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast @@ -12,14 +12,14 @@ SpaceAfter( @50-92, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature.expr.result-ast index b63b9570b9..46cf7e0a00 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-10, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature_with_comment.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature_with_comment.expr.result-ast index 238eeb51bc..65f707ec00 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature_with_comment.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_type_signature_with_comment.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-19, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/negative_in_apply_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/negative_in_apply_def.expr.result-ast index b86b0ccf9b..fdc544916b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/negative_in_apply_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/negative_in_apply_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-9, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index 5fc26b7458..b6828bfaff 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-115, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -30,10 +30,10 @@ Defs { @11-93, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast index dd2a7dee12..a46baaea9a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-9, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_and_spaces_before_less_than.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_and_spaces_before_less_than.expr.result-ast index 35c80b7943..58e60e2b64 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_and_spaces_before_less_than.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_and_spaces_before_less_than.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-13, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast index 16b7434654..4fc7385b18 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast @@ -34,10 +34,10 @@ Full( @53-100, ], space_before: [ - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 2, length: 1 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_alias_application.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_alias_application.expr.result-ast index ebd27bd9d5..0fda46101e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_alias_application.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_alias_application.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-6, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_def.expr.result-ast index d705f6304b..c09c9c5d37 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_type_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-4, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast index 9ad363d54d..0ca8b776fa 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast @@ -52,12 +52,12 @@ Full( @157-187, ], space_before: [ - Slice { start: 0, length: 3 }, - Slice { start: 6, length: 2 }, + Slice { start: 0, length: 3 }, + Slice { start: 6, length: 2 }, ], space_after: [ - Slice { start: 3, length: 3 }, - Slice { start: 8, length: 2 }, + Slice { start: 3, length: 3 }, + Slice { start: 8, length: 2 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast index fbaced4ce3..c0d9727db3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast @@ -9,10 +9,10 @@ SpaceBefore( @18-21, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast index 89666c0613..ef05105d25 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast @@ -9,10 +9,10 @@ SpaceBefore( @18-23, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast index 21726b803b..903afc5203 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-22, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast index 40a51299c4..5f771725bd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast @@ -26,28 +26,28 @@ SpaceAfter( @401-427, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, - Slice { start: 4, length: 2 }, - Slice { start: 6, length: 2 }, - Slice { start: 8, length: 2 }, - Slice { start: 10, length: 2 }, - Slice { start: 12, length: 2 }, - Slice { start: 14, length: 2 }, - Slice { start: 16, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, + Slice { start: 2, length: 2 }, + Slice { start: 4, length: 2 }, + Slice { start: 6, length: 2 }, + Slice { start: 8, length: 2 }, + Slice { start: 10, length: 2 }, + Slice { start: 12, length: 2 }, + Slice { start: 14, length: 2 }, + Slice { start: 16, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 0 }, - Slice { start: 6, length: 0 }, - Slice { start: 8, length: 0 }, - Slice { start: 10, length: 0 }, - Slice { start: 12, length: 0 }, - Slice { start: 14, length: 0 }, - Slice { start: 16, length: 0 }, - Slice { start: 18, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 0 }, + Slice { start: 6, length: 0 }, + Slice { start: 8, length: 0 }, + Slice { start: 10, length: 0 }, + Slice { start: 12, length: 0 }, + Slice { start: 14, length: 0 }, + Slice { start: 16, length: 0 }, + Slice { start: 18, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast index ecb948e64d..642c75f5ca 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-9, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast index 1d9a396404..5fe49a3ff1 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-53, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast index 59d6eb1675..d971a44f86 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-29, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast index 4cced72b81..fe627e2b54 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-22, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast index 3e94afad7a..453657569d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-17, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast index 3315ef9ddc..8a5e2bfa59 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-23, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_type_def_apply.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_type_def_apply.expr.result-ast index 4a42d096a8..64f9667ea3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_type_def_apply.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_type_def_apply.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-8, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast index 15d8c5f845..7b6b3ded59 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-5, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast index ee5f397811..9b37ffe977 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-6, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast index bf1236033e..2f51d31a2e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast index 0f2e29f45c..4ad9db97d6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast index 5b551e9d95..521b4e9399 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_question.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-166, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast index 38ed746233..20e2e46135 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast @@ -11,12 +11,12 @@ SpaceBefore( @31-36, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast index 8a76150ddf..b8c1cc89ce 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_field_bang.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-32, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast index 12ba5102e7..eb35714507 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-122, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast index 7eaa142894..5f3ed09000 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-77, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast index 643a2023cc..c320411f28 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_updater_literal_apply.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-42, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/return_in_if.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/return_in_if.expr.result-ast index 410e650571..9b0bf5e3c0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/return_in_if.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/return_in_if.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-127, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -36,10 +36,10 @@ SpaceAfter( @29-110, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/return_in_static_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/return_in_static_def.expr.result-ast index 94e49d5f7f..f4a4a61040 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/return_in_static_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/return_in_static_def.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-142, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -30,10 +30,10 @@ SpaceAfter( @21-125, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -70,10 +70,10 @@ SpaceAfter( @67-72, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/return_in_when.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/return_in_when.expr.result-ast index ffca41041b..b563809d59 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/return_in_when.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/return_in_when.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-154, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], @@ -36,10 +36,10 @@ SpaceAfter( @29-136, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/return_only_statement.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/return_only_statement.expr.result-ast index 0041535b2d..98d7aa6820 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/return_only_statement.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/return_only_statement.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast index 7b5bc06924..6bf06e0708 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast @@ -16,20 +16,20 @@ Defs { @234-266, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, - Slice { start: 2, length: 2 }, - Slice { start: 4, length: 1 }, - Slice { start: 5, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, + Slice { start: 2, length: 2 }, + Slice { start: 4, length: 1 }, + Slice { start: 5, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 0 }, - Slice { start: 5, length: 0 }, - Slice { start: 6, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 0 }, + Slice { start: 5, length: 0 }, + Slice { start: 6, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast index 3a4876afaa..4d53368401 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast @@ -36,10 +36,10 @@ Full( @39-65, ], space_before: [ - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 2, length: 1 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.result-ast index 361490271c..d944ce8e1d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.result-ast @@ -10,14 +10,14 @@ Defs { @44-57, ], space_before: [ - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 3 }, - Slice { start: 4, length: 1 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 3 }, + Slice { start: 4, length: 1 }, ], space_after: [ - Slice { start: 1, length: 0 }, - Slice { start: 4, length: 0 }, - Slice { start: 5, length: 2 }, + Slice { start: 1, length: 0 }, + Slice { start: 4, length: 0 }, + Slice { start: 5, length: 2 }, ], spaces: [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast index a22cd1c19f..fa36420396 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast @@ -6,10 +6,10 @@ Defs { @0-49, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 1 }, ], spaces: [ Newline, @@ -32,12 +32,12 @@ Defs { @26-39, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast index 61036c551b..95de78c1a0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast @@ -46,12 +46,12 @@ Full( @76-220, ], space_before: [ - Slice { start: 0, length: 2 }, - Slice { start: 2, length: 2 }, + Slice { start: 0, length: 2 }, + Slice { start: 2, length: 2 }, ], space_after: [ - Slice { start: 2, length: 0 }, - Slice { start: 4, length: 2 }, + Slice { start: 2, length: 0 }, + Slice { start: 4, length: 2 }, ], spaces: [ Newline, @@ -95,12 +95,12 @@ Full( @162-205, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 3 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 3 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 3, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 3, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast index eb059a6798..ffc0bce369 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast @@ -50,10 +50,10 @@ Full( @88-204, ], space_before: [ - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 2, length: 1 }, + Slice { start: 2, length: 1 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast index c544346d5d..45b354596a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast index 303e59b8ad..f926dbc12f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tag_destructure_bang_no_space.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-32, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_access_after_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_access_after_ident.expr.result-ast index f3a6dbb1d5..5272a96f27 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_access_after_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_access_after_ident.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-15, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast index f98f394b2d..ac20ecc5ca 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_destructure_bang.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-32, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast index 07482202ab..b5a12c87c4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-39, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast index 92c3f3e03f..d744a7f198 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/tuple_type_ext.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-41, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast index 4cb12e863e..08c99c9fb3 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast @@ -11,12 +11,12 @@ SpaceBefore( @24-29, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast index f85036bbe2..20f71709d4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_decl_with_underscore.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-30, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast index 3c2f8f24b3..34da00ded4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-17, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast index 36bcc2decb..cdde5c91f9 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/type_signature_function_def.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-42, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast index 1206669bdd..855fff207a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast @@ -16,18 +16,18 @@ SpaceAfter( @73-128, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, - Slice { start: 1, length: 1 }, - Slice { start: 2, length: 1 }, - Slice { start: 3, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, + Slice { start: 1, length: 1 }, + Slice { start: 2, length: 1 }, + Slice { start: 3, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, - Slice { start: 2, length: 0 }, - Slice { start: 3, length: 0 }, - Slice { start: 4, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, + Slice { start: 2, length: 0 }, + Slice { start: 3, length: 0 }, + Slice { start: 4, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/value_def_confusion.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/value_def_confusion.expr.result-ast index e345a31d94..006ae8efa6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/value_def_confusion.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/value_def_confusion.expr.result-ast @@ -9,12 +9,12 @@ Defs( @4-8, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 1 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 1 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 1, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 1, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_assignment.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_assignment.expr.result-ast index e8e18b170a..ae141c0cc6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_assignment.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_assignment.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-25, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function.expr.result-ast index e1b78fcc9c..b7b59bf869 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-43, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function_python_style_indent.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function_python_style_indent.expr.result-ast index 0671a83da0..d06a42df79 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function_python_style_indent.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_function_python_style_indent.expr.result-ast @@ -7,10 +7,10 @@ Defs( @0-33, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast index 9d0c667000..e0cfd980ca 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-38, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast index 0bc4dc2d01..f77a9a351b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast @@ -10,12 +10,12 @@ SpaceAfter( @75-154, ], space_before: [ - Slice { start: 0, length: 0 }, - Slice { start: 0, length: 2 }, + Slice { start: 0, length: 0 }, + Slice { start: 0, length: 2 }, ], space_after: [ - Slice { start: 0, length: 0 }, - Slice { start: 2, length: 0 }, + Slice { start: 0, length: 0 }, + Slice { start: 2, length: 0 }, ], spaces: [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast index 66b7a5b3cf..2b6ecdf721 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-73, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast index 0a6edab08b..c001f7d74f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-92, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast index 4cfc12e509..86afbf5631 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-26, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast index ff2dbc064e..bfa825be89 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-40, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast index 09d1f93c6b..88031feb51 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast @@ -8,10 +8,10 @@ SpaceAfter( @0-39, ], space_before: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], space_after: [ - Slice { start: 0, length: 0 }, + Slice { start: 0, length: 0 }, ], spaces: [], type_defs: [], diff --git a/crates/compiler/types/src/subs.rs b/crates/compiler/types/src/subs.rs index c64bf9fd3c..f350c2f609 100644 --- a/crates/compiler/types/src/subs.rs +++ b/crates/compiler/types/src/subs.rs @@ -975,7 +975,7 @@ impl From for Option { } /// Marks whether a when expression is exhaustive using a variable. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct ExhaustiveMark(Variable); impl ExhaustiveMark { @@ -2303,7 +2303,7 @@ roc_error_macros::assert_sizeof_default!((Variable, Option), 4 * 8); roc_error_macros::assert_copyable!(Content); roc_error_macros::assert_copyable!(Descriptor); -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum Content { /// A type variable which the user did not name in an annotation, /// @@ -2343,7 +2343,7 @@ pub enum Content { /// if b then f else g /// /// has the type {} -[f, g]-> {} where [f, g] is the solved lambda set. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct LambdaSet { /// The resolved lambda symbols we know. pub solved: UnionLambdas, @@ -2382,7 +2382,7 @@ pub struct LambdaSet { pub ambient_function: Variable, } -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct AliasVariables { pub variables_start: u32, pub all_variables_len: u16, @@ -2532,7 +2532,7 @@ impl Content { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum TagExt { /// This tag extension variable measures polymorphism in the openness of the tag, /// or the lack thereof. It can only be unified with @@ -2581,7 +2581,7 @@ impl TagExt { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum FlatType { Apply(Symbol, VariableSubsSlice), Func(VariableSubsSlice, Variable, Variable, Variable), @@ -2700,7 +2700,7 @@ impl Label for Symbol { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnionLabels { pub(crate) length: u16, pub(crate) labels_start: u32, @@ -3045,7 +3045,7 @@ pub fn is_empty_tag_union(subs: &Subs, mut var: Variable) -> bool { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct RecordFields { pub length: u16, pub field_names_start: u32, @@ -3258,7 +3258,7 @@ fn is_empty_record(subs: &Subs, mut var: Variable) -> bool { } } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct TupleElems { pub length: u16, diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index fffecf9338..9af9f5aa28 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -306,7 +306,7 @@ impl FromIterator for AbilitySet { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct OptAbleVar { pub var: Variable, pub opt_abilities: Option, @@ -3575,6 +3575,13 @@ impl AliasKind { AliasKind::Opaque => "opaque", } } + + pub fn as_str_plural(&self) -> &'static str { + match self { + AliasKind::Structural => "aliases", + AliasKind::Opaque => "opaque types", + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -3613,7 +3620,7 @@ pub enum MemberImpl { Error, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Alias { pub region: Region, pub type_variables: Vec>, diff --git a/crates/glue/Cargo.toml b/crates/glue/Cargo.toml index 2f3d92d40c..6e491f5169 100644 --- a/crates/glue/Cargo.toml +++ b/crates/glue/Cargo.toml @@ -27,7 +27,6 @@ roc_types = { path = "../compiler/types" } bumpalo.workspace = true fnv.workspace = true -indexmap.workspace = true libc.workspace = true libloading.workspace = true strum.workspace = true diff --git a/crates/limits/Cargo.toml b/crates/limits/Cargo.toml new file mode 100644 index 0000000000..b31d24f7d4 --- /dev/null +++ b/crates/limits/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "limits" +description = "Things like maximum number of arguments supported per function, etc." +path = "src/limits.rs" + +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +[dependencies] + +[dev-dependencies] diff --git a/crates/limits/src/limits.rs b/crates/limits/src/limits.rs new file mode 100644 index 0000000000..3107a99ea7 --- /dev/null +++ b/crates/limits/src/limits.rs @@ -0,0 +1,7 @@ +/// See specialize_type.rs for where these numbers comes from. +pub const MAX_ARGS_PER_FUNCTION: usize = (i16::MAX - 1) as usize; +pub const MAX_FIELDS_PER_RECORD: usize = u16::MAX as usize; +pub const MAX_ELEMS_PER_TUPLE: usize = u16::MAX as usize; +pub const MAX_TAG_UNION_VARIANTS: usize = u16::MAX as usize; // We don't support discriminants bigger than u16. +pub const MAX_DEC_INTEGER_COMPONENT: usize = (); // TODO figure out what this is +pub const MAX_DEC_DECIMAL_COMPONENT: usize = (); // TODO figure out what this is diff --git a/crates/repl_cli/Cargo.toml b/crates/repl_cli/Cargo.toml index c69ba39883..3ada03b64c 100644 --- a/crates/repl_cli/Cargo.toml +++ b/crates/repl_cli/Cargo.toml @@ -41,7 +41,6 @@ libloading.workspace = true rustyline-derive.workspace = true rustyline.workspace = true target-lexicon.workspace = true -unicode-segmentation.workspace = true [lib] name = "roc_repl_cli" diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index 62a36dda73..5e34152408 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -1512,6 +1512,7 @@ fn to_dbg_or_expect_report<'a>( to_space_report(alloc, lines, filename, err, *pos) } + roc_parse::parser::EExpect::DbgArity(_) => todo!(), roc_parse::parser::EExpect::Dbg(_) => unreachable!("another branch would be taken"), roc_parse::parser::EExpect::Expect(_) => unreachable!("another branch would be taken"), diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index c8105a98c6..2fe6c652a0 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -676,8 +676,8 @@ pub fn cyclic_alias<'b>( ) -> (RocDocBuilder<'b>, String) { let when_is_recursion_legal = alloc.reflow("Recursion in ") - .append(alloc.reflow(alias_kind.as_str())) - .append(alloc.reflow("es is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive.")); + .append(alloc.reflow(alias_kind.as_str_plural())) + .append(alloc.reflow(" is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive.")); let doc = if others.is_empty() { alloc.stack([ diff --git a/crates/soa/src/lib.rs b/crates/soa/src/lib.rs index 1c3c5a52c1..cf8000d5d2 100644 --- a/crates/soa/src/lib.rs +++ b/crates/soa/src/lib.rs @@ -3,7 +3,11 @@ mod either_index; mod soa_index; mod soa_slice; +mod soa_slice2; +mod soa_slice3; pub use either_index::*; pub use soa_index::*; -pub use soa_slice::*; +pub use soa_slice::{NonEmptySlice, Slice}; +pub use soa_slice2::Slice2; +pub use soa_slice3::Slice3; diff --git a/crates/soa/src/soa_index.rs b/crates/soa/src/soa_index.rs index 3f9e62b782..9d955ace0f 100644 --- a/crates/soa/src/soa_index.rs +++ b/crates/soa/src/soa_index.rs @@ -1,4 +1,5 @@ use core::{ + any, cmp::Ordering, fmt::{self, Formatter}, hash::{Hash, Hasher}, @@ -39,7 +40,7 @@ impl Ord for Index { impl fmt::Debug for Index { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Index({})", self.index) + write!(f, "Index<{}>({})", any::type_name::(), self.index) } } diff --git a/crates/soa/src/soa_slice.rs b/crates/soa/src/soa_slice.rs index e00381ee77..20540e4d80 100644 --- a/crates/soa/src/soa_slice.rs +++ b/crates/soa/src/soa_slice.rs @@ -1,4 +1,9 @@ -use core::{fmt, marker::PhantomData, ops::Range}; +use core::{ + fmt, + marker::PhantomData, + num::{NonZeroU16, NonZeroUsize}, + ops::Range, +}; use crate::soa_index::Index; @@ -18,8 +23,10 @@ impl fmt::Debug for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - "Slice {{ start: {}, length: {} }}", - self.start, self.length + "Slice<{}> {{ start: {}, length: {} }}", + core::any::type_name::(), + self.start, + self.length ) } } @@ -99,6 +106,18 @@ impl Slice { _marker: PhantomData, } } + + pub const fn truncate(&self, length: u16) -> Self { + Self { + start: self.start, + length, + _marker: PhantomData, + } + } + + pub fn as_nonempty_slice(&self) -> Option> { + NonZeroU16::new(self.length).map(|nonzero_len| NonEmptySlice::new(self.start, nonzero_len)) + } } impl IntoIterator for Slice { @@ -147,3 +166,111 @@ impl ExactSizeIterator for SliceIterator {} pub trait GetSlice { fn get_slice(&self, slice: Slice) -> &[T]; } + +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct NonEmptySlice { + inner: Slice, +} + +impl fmt::Debug for NonEmptySlice { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Copy for NonEmptySlice {} + +impl Clone for NonEmptySlice { + fn clone(&self) -> Self { + *self + } +} + +impl NonEmptySlice { + pub const fn start(self) -> u32 { + self.inner.start() + } + + pub fn advance(&mut self, amount: u32) { + self.inner.advance(amount); + } + + pub fn get_slice<'a>(&self, elems: &'a [T]) -> &'a [T] { + self.inner.get_slice(elems) + } + + pub fn get_slice_mut<'a>(&self, elems: &'a mut [T]) -> &'a mut [T] { + self.inner.get_slice_mut(elems) + } + + #[inline(always)] + pub const fn indices(&self) -> Range { + self.inner.indices() + } + + pub const fn len(&self) -> NonZeroUsize { + // Safety: we only accept a nonzero length on construction + unsafe { NonZeroUsize::new_unchecked(self.inner.len()) } + } + + pub const fn new(start: u32, length: NonZeroU16) -> Self { + Self { + inner: Slice { + start, + length: length.get(), + _marker: PhantomData, + }, + } + } + + /// # Safety + /// + /// The caller must ensure that the length is nonzero + pub const unsafe fn new_unchecked(start: u32, length: u16) -> Self { + Self { + inner: Slice { + start, + length, + _marker: PhantomData, + }, + } + } + + pub const fn from_slice(slice: Slice) -> Option { + // Using a match here because Option::map is not const + match NonZeroU16::new(slice.length) { + Some(len) => Some(Self::new(slice.start, len)), + None => None, + } + } + + /// # Safety + /// + /// The caller must ensure that the length is nonzero + pub const unsafe fn from_slice_unchecked(slice: Slice) -> Self { + Self::new(slice.start, NonZeroU16::new_unchecked(slice.length)) + } + + pub const fn truncate(&self, length: NonZeroU16) -> Self { + Self { + inner: Slice { + start: self.inner.start, + length: length.get(), + _marker: PhantomData, + }, + } + } + + pub fn as_slice(&self) -> Slice { + self.inner + } +} + +impl IntoIterator for NonEmptySlice { + type Item = Index; + type IntoIter = SliceIterator; + + fn into_iter(self) -> Self::IntoIter { + self.inner.into_iter() + } +} diff --git a/crates/soa/src/soa_slice2.rs b/crates/soa/src/soa_slice2.rs new file mode 100644 index 0000000000..af34b1d273 --- /dev/null +++ b/crates/soa/src/soa_slice2.rs @@ -0,0 +1,139 @@ +use core::{fmt, marker::PhantomData}; + +use crate::{soa_index::Index, soa_slice::Slice}; + +/// Two slices of the same length, each based on a different +/// offset into the same array (rather than a pointer). +/// +/// Unlike a Rust slice, this is a u32 offset +/// rather than a pointer, and the length is u16. +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct Slice2 { + pub start1: u32, + pub start2: u32, + pub length: u16, + pub _marker: core::marker::PhantomData<(T, U)>, +} + +impl fmt::Debug for Slice2 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Slice2<{}, {}> {{ start1: {}, start2: {}, length: {} }}", + core::any::type_name::(), + core::any::type_name::(), + self.start1, + self.start2, + self.length + ) + } +} + +// derive of copy and clone does not play well with PhantomData + +impl Copy for Slice2 {} + +impl Clone for Slice2 { + fn clone(&self) -> Self { + *self + } +} + +impl Default for Slice2 { + fn default() -> Self { + Self::empty() + } +} + +impl Slice2 { + pub const fn empty() -> Self { + Self { + start1: 0, + start2: 0, + length: 0, + _marker: PhantomData, + } + } + + pub const fn slice_first(self) -> Slice { + Slice { + start: self.start1, + length: self.length, + _marker: PhantomData, + } + } + + pub const fn slice_second(self) -> Slice { + Slice { + start: self.start2, + length: self.length, + _marker: PhantomData, + } + } + + pub const fn len(&self) -> usize { + self.length as usize + } + + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub const fn new(start1: u32, start2: u32, length: u16) -> Self { + Self { + start1, + start2, + length, + _marker: PhantomData, + } + } +} + +impl IntoIterator for Slice2 { + type Item = (Index, Index); + type IntoIter = SliceIterator; + + fn into_iter(self) -> Self::IntoIter { + SliceIterator { + slice: self, + offset: 0, + } + } +} + +pub struct SliceIterator { + slice: Slice2, + offset: u32, +} + +impl Iterator for SliceIterator { + type Item = (Index, Index); + + fn next(&mut self) -> Option { + let offset = self.offset; + + if offset < self.slice.length as u32 { + let index1 = Index { + index: self.slice.start1 + offset, + _marker: PhantomData, + }; + let index2 = Index { + index: self.slice.start2 + offset, + _marker: PhantomData, + }; + + self.offset += 1; + + Some((index1, index2)) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.slice.length as u32 - self.offset) as usize; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for SliceIterator {} diff --git a/crates/soa/src/soa_slice3.rs b/crates/soa/src/soa_slice3.rs new file mode 100644 index 0000000000..ec0c554b76 --- /dev/null +++ b/crates/soa/src/soa_slice3.rs @@ -0,0 +1,156 @@ +use core::{fmt, marker::PhantomData}; + +use crate::{soa_index::Index, soa_slice::Slice}; + +/// Three slices of the same length, each based on a different +/// offset into the same array (rather than a pointer). +/// +/// Unlike a Rust slice, this is a u32 offset +/// rather than a pointer, and the length is u16. +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct Slice3 { + pub start1: u32, + pub start2: u32, + pub start3: u32, + pub length: u16, + pub _marker: core::marker::PhantomData<(T, U, V)>, +} + +impl fmt::Debug for Slice3 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Slice3<{}, {}, {}> {{ start1: {}, start2: {}, start3: {}, length: {} }}", + core::any::type_name::(), + core::any::type_name::(), + core::any::type_name::(), + self.start1, + self.start2, + self.start3, + self.length + ) + } +} + +// derive of copy and clone does not play well with PhantomData + +impl Copy for Slice3 {} + +impl Clone for Slice3 { + fn clone(&self) -> Self { + *self + } +} + +impl Default for Slice3 { + fn default() -> Self { + Self::empty() + } +} + +impl Slice3 { + pub const fn empty() -> Self { + Self { + start1: 0, + start2: 0, + start3: 0, + length: 0, + _marker: PhantomData, + } + } + + pub const fn slice_first(self) -> Slice { + Slice { + start: self.start1, + length: self.length, + _marker: PhantomData, + } + } + + pub const fn slice_second(self) -> Slice { + Slice { + start: self.start2, + length: self.length, + _marker: PhantomData, + } + } + + pub const fn slice_third(self) -> Slice { + Slice { + start: self.start3, + length: self.length, + _marker: PhantomData, + } + } + + pub const fn len(&self) -> usize { + self.length as usize + } + + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub const fn new(start1: u32, start2: u32, start3: u32, length: u16) -> Self { + Self { + start1, + start2, + start3, + length, + _marker: PhantomData, + } + } +} + +impl IntoIterator for Slice3 { + type Item = (Index, Index, Index); + type IntoIter = SliceIterator; + + fn into_iter(self) -> Self::IntoIter { + SliceIterator { + slice: self, + offset: 0, + } + } +} + +pub struct SliceIterator { + slice: Slice3, + offset: u32, +} + +impl Iterator for SliceIterator { + type Item = (Index, Index, Index); + + fn next(&mut self) -> Option { + let offset = self.offset; + + if offset < self.slice.length as u32 { + let index1 = Index { + index: self.slice.start1 + offset, + _marker: PhantomData, + }; + let index2 = Index { + index: self.slice.start2 + offset, + _marker: PhantomData, + }; + let index3 = Index { + index: self.slice.start3 + offset, + _marker: PhantomData, + }; + + self.offset += 1; + + Some((index1, index2, index3)) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = (self.slice.length as u32 - self.offset) as usize; + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for SliceIterator {} diff --git a/crates/test_compile/Cargo.toml b/crates/test_compile/Cargo.toml new file mode 100644 index 0000000000..56b4a2e0ba --- /dev/null +++ b/crates/test_compile/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "test_compile" +description = "Utility functions for starting from strings of code and compiling various IRs." + +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +[dependencies] +roc_builtins = { path = "../compiler/builtins" } +roc_derive = { path = "../compiler/derive", features = [ + "debug-derived-symbols", +] } +roc_region = { path = "../compiler/region" } +roc_collections = { path = "../compiler/collections" } +roc_load = { path = "../compiler/load" } +roc_parse = { path = "../compiler/parse" } +roc_can = { path = "../compiler/can" } +roc_module = { path = "../compiler/module" } +roc_types = { path = "../compiler/types" } +roc_problem = { path = "../compiler/problem" } +roc_constrain = { path = "../compiler/constrain" } +roc_reporting = { path = "../reporting" } +roc_target = { path = "../compiler/roc_target" } +roc_solve = { path = "../compiler/solve" } +roc_solve_problem = { path = "../compiler/solve_problem" } + +roc_specialize_types.workspace = true +bumpalo.workspace = true + +[dev-dependencies] +pretty_assertions.workspace = true diff --git a/crates/test_compile/src/deindent.rs b/crates/test_compile/src/deindent.rs new file mode 100644 index 0000000000..9e7d23eae5 --- /dev/null +++ b/crates/test_compile/src/deindent.rs @@ -0,0 +1,220 @@ +use bumpalo::Bump; + +/// The purpose of this function is to let us run tests like this: +/// +/// ```rust,ignore +/// run_some_test(r#" +/// x = 1 +/// +/// x +/// "#) +/// +/// ...without needing to call a macro like `indoc!` to deal with the fact that +/// multiline Rust string literals preserve all the indented spaces. +/// +/// This function removes the indentation by removing leading newlines (e.g. after +/// the `(r#"` opening) and then counting how many spaces precede the first line +/// (e.g. `" x = 1"` here) and trimming that many spaces from the beginning +/// of each subsequent line. The end of the string is then trimmed normally, and +/// any remaining empty lines are left empty. +pub fn trim_and_deindent<'a>(arena: &'a Bump, input: &'a str) -> &'a str { + let newline_count = input.chars().filter(|&ch| ch == '\n').count(); + + // If it's a single-line string, return it without allocating anything. + if newline_count == 0 { + return input.trim(); // Trim to remove spaces + } + + // Trim leading blank lines - we expect at least one, because the opening line will be `(r#"` + // (Also, there may be stray blank lines at the start, which this will trim off too.) + let mut lines = bumpalo::collections::Vec::with_capacity_in(newline_count + 1, arena); + + for line in input + .lines() + // Keep skipping until we hit a line that is neither empty nor all spaces. + .skip_while(|line| line.chars().all(|ch| ch == ' ')) + { + lines.push(line); + } + + // Drop trailing blank lines + while lines + .last() + .map_or(false, |line| line.chars().all(|ch| ch == ' ')) + { + lines.pop(); + } + + // Now that we've trimmed leading and trailing blank lines, + // Find the smallest indent of the remaining lines. That's our indentation amount. + let smallest_indent = lines + .iter() + .filter(|line| !line.trim().is_empty()) + .map(|line| line.chars().take_while(|&ch| ch == ' ').count()) + .min() + .unwrap_or(0); + + // Remove this amount of indentation from each line. + let mut final_str_len = 0; + + lines.iter_mut().for_each(|line| { + if line.starts_with(' ') { + *line = line.get(smallest_indent..).unwrap_or(""); + } + final_str_len += line.len() + 1; // +1 for the newline that will be added to the end of this line. + }); + + // Convert lines into a bumpalo::String + let mut answer = bumpalo::collections::String::with_capacity_in(final_str_len, arena); + + // Unconditionally push a newline after each line we add. We'll trim off the last one before we return. + for line in lines { + answer.push_str(line); + answer.push('\n'); + } + + // Trim off the extra newline we added at the end. (Saturate to 0 if we ended up with no lines.) + &answer.into_bump_str()[..final_str_len.saturating_sub(1)] +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn test_single_line_input() { + let input = "single line"; + assert_eq!(trim_and_deindent(&Bump::new(), input), "single line"); + } + + #[test] + fn test_multiline_with_indentation() { + let input = r#" + x = 1 + + x + "#; + let expected = "x = 1\n\nx"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_multiline_with_varying_indentation() { + let input = r#" + x = 1 + y = 2 + z = 3 + "#; + let expected = "x = 1\n y = 2\nz = 3"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_multiline_with_empty_lines() { + let input = r#" + x = 1 + + y = 2 + + z = 3 + "#; + let expected = "x = 1\n\ny = 2\n\nz = 3"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_without_leading_newline() { + let input = " x = 1\n y = 2"; + let expected = "x = 1\ny = 2"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_multiple_leading_newlines() { + let input = "\n\n\n x = 1\n y = 2"; + let expected = "x = 1\ny = 2"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_mixed_indentation() { + let input = r#" + x = 1 + y = 2 + z = 3 + "#; + let expected = " x = 1\ny = 2\n z = 3"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_only_spaces() { + let input = " "; + let expected = ""; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_only_newlines() { + let input = "\n\n\n"; + let expected = ""; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_tabs() { + let input = "\t\tx = 1\n\t\ty = 2"; + let expected = "\t\tx = 1\n\t\ty = 2"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_mixed_spaces_and_tabs() { + let input = " \tx = 1\n \ty = 2"; + let expected = "\tx = 1\n\ty = 2"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_trailing_spaces() { + let input = " x = 1 \n y = 2 "; + let expected = "x = 1 \ny = 2 "; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_empty_lines_and_spaces() { + let input = " x = 1\n \n y = 2"; + let expected = "x = 1\n\ny = 2"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_different_indentation_levels() { + let input = " x = 1\n y = 2\n z = 3"; + let expected = " x = 1\n y = 2\nz = 3"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_non_space_characters_at_start() { + let input = "x = 1\n y = 2\n z = 3"; + let expected = "x = 1\n y = 2\n z = 3"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_empty_input() { + let input = ""; + let expected = ""; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } + + #[test] + fn test_input_with_only_one_indented_line() { + let input = " x = 1"; + let expected = "x = 1"; + assert_eq!(trim_and_deindent(&Bump::new(), input), expected); + } +} diff --git a/crates/test_compile/src/help_can.rs b/crates/test_compile/src/help_can.rs new file mode 100644 index 0000000000..8b42b069ee --- /dev/null +++ b/crates/test_compile/src/help_can.rs @@ -0,0 +1,142 @@ +use crate::help_parse::ParseExpr; +use bumpalo::Bump; +use roc_can::{ + desugar, + env::Env, + expr::{canonicalize_expr, Expr, Output}, + scope::Scope, +}; +use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, PackageModuleIds, Symbol}; +use roc_problem::can::Problem; +use roc_region::all::{Loc, Region}; +use roc_types::{ + subs::{VarStore, Variable}, + types::{AliasVar, Type}, +}; +use std::path::Path; + +#[derive(Debug)] +pub struct CanExprOut { + pub expr: Expr, + pub region: Region, + pub output: Output, + pub problems: Vec, + pub home: ModuleId, + pub interns: Interns, + pub var_store: VarStore, + pub var: Variable, +} + +pub struct CanExpr { + pub(crate) home: ModuleId, + + parse_expr: ParseExpr, +} + +impl Default for CanExpr { + fn default() -> Self { + Self { + parse_expr: ParseExpr::default(), + home: test_home(), + } + } +} + +fn test_home() -> ModuleId { + ModuleIds::default().get_or_insert(&"Test".into()) +} + +impl CanExpr { + pub fn can_expr<'a>(&'a self, input: &'a str) -> CanExprOut { + match self.parse_expr.parse_loc_expr(input) { + Ok(loc_expr) => { + let mut var_store = VarStore::default(); + let var = var_store.fresh(); + let qualified_module_ids = PackageModuleIds::default(); + let home = test_home(); + + let mut scope = Scope::new( + home, + "TestPath".into(), + IdentIds::default(), + Default::default(), + ); + + let dep_idents = IdentIds::exposed_builtins(0); + let mut env = Env::new( + self.arena(), + input, + home, + Path::new("Test.roc"), + &dep_idents, + &qualified_module_ids, + None, + roc_can::env::FxMode::PurityInference, + ); + + // Desugar operators (convert them to Apply calls, taking into account + // operator precedence and associativity rules), before doing other canonicalization. + // + // If we did this *during* canonicalization, then each time we + // visited a BinOp node we'd recursively try to apply this to each of its nested + // operators, and then again on *their* nested operators, ultimately applying the + // rules multiple times unnecessarily. + let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr); + + scope.add_alias( + Symbol::NUM_INT, + Region::zero(), + vec![Loc::at_zero(AliasVar::unbound( + "a".into(), + Variable::EMPTY_RECORD, + ))], + vec![], + Type::EmptyRec, + roc_types::types::AliasKind::Structural, + ); + + let (loc_expr, output) = canonicalize_expr( + &mut env, + &mut var_store, + &mut scope, + Region::zero(), + &loc_expr.value, + ); + + let mut all_ident_ids = IdentIds::exposed_builtins(1); + all_ident_ids.insert(home, scope.locals.ident_ids); + + let interns = Interns { + module_ids: env.qualified_module_ids.clone().into_module_ids(), + all_ident_ids, + }; + + CanExprOut { + expr: loc_expr.value, + region: loc_expr.region, + output, + problems: env.problems, + home: env.home, + var_store, + interns, + var, + } + } + Err(syntax_error) => { + panic!("Unexpected syntax error: {:?}", syntax_error); + } + } + } + + pub fn into_arena(self) -> Bump { + self.parse_expr.into_arena() + } + + pub fn arena(&self) -> &Bump { + self.parse_expr.arena() + } + + pub fn home(&self) -> ModuleId { + self.home + } +} diff --git a/crates/test_compile/src/help_constrain.rs b/crates/test_compile/src/help_constrain.rs new file mode 100644 index 0000000000..223c8d77fe --- /dev/null +++ b/crates/test_compile/src/help_constrain.rs @@ -0,0 +1,81 @@ +use crate::CanExpr; +use bumpalo::Bump; +use roc_can::{ + constraint::{Constraint, Constraints}, + expected::Expected, + expr::Expr, +}; +use roc_constrain::expr::{constrain_expr, Env}; +use roc_module::symbol::ModuleId; +use roc_region::all::Region; +use roc_types::{ + subs::{Subs, Variable}, + types::Types, +}; + +#[derive(Debug)] +pub struct ConstrainedExprOut { + pub expr: Expr, + pub var: Variable, + pub constraint: Constraint, + pub constraints: Constraints, + pub types: Types, + pub region: Region, +} + +#[allow(dead_code)] +#[derive(Default)] +pub struct ConstrainedExpr { + can_expr: CanExpr, + subs: Subs, + constraints: Constraints, +} + +impl ConstrainedExpr { + pub fn constrain_expr<'a>(&'a self, input: &'a str) -> ConstrainedExprOut { + let mut can_expr_out = self.can_expr.can_expr(input); + + assert_eq!( + can_expr_out.problems, + Vec::new(), + "Encountered unexpected canonicalization problem when trying to constrain expr" + ); + + let mut types = Types::default(); + let mut constraints = Constraints::default(); + let var = can_expr_out.var_store.fresh(); + let var_index = constraints.push_variable(var); + let expected = constraints.push_expected_type(Expected::NoExpectation(var_index)); + let mut env = Env::new(self.can_expr.home); + + let constraint = constrain_expr( + &mut types, + &mut constraints, + &mut env, + can_expr_out.region, + &can_expr_out.expr, + expected, + ); + + ConstrainedExprOut { + expr: can_expr_out.expr, + var: can_expr_out.var, + constraint, + constraints, + types, + region: can_expr_out.region, + } + } + + pub fn into_arena(self) -> Bump { + self.can_expr.into_arena() + } + + pub fn arena(&self) -> &Bump { + self.can_expr.arena() + } + + pub fn home(&self) -> ModuleId { + self.can_expr.home() + } +} diff --git a/crates/test_compile/src/help_parse.rs b/crates/test_compile/src/help_parse.rs new file mode 100644 index 0000000000..081807b217 --- /dev/null +++ b/crates/test_compile/src/help_parse.rs @@ -0,0 +1,64 @@ +use bumpalo::Bump; +use roc_parse::{ + ast, + blankspace::space0_before_optional_after, + expr::{expr_end, loc_expr_block}, + parser::{skip_second, EExpr, Parser, SourceError, SyntaxError}, + state::State, +}; +use roc_region::all::{Loc, Position}; + +use crate::deindent::trim_and_deindent; + +pub struct ParseExpr { + arena: Bump, +} + +impl Default for ParseExpr { + fn default() -> Self { + Self { + arena: Bump::with_capacity(4096), + } + } +} + +impl ParseExpr { + pub fn parse_expr<'a>(&'a self, input: &'a str) -> Result, SyntaxError<'a>> { + self.parse_loc_expr(input) + .map(|loc_expr| loc_expr.value) + .map_err(|e| e.problem) + } + + pub fn parse_loc_expr<'a>( + &'a self, + input: &'a str, + ) -> Result>, SourceError<'a, SyntaxError<'a>>> { + let original_bytes = trim_and_deindent(&self.arena, input).as_bytes(); + let state = State::new(original_bytes); + + let parser = skip_second( + space0_before_optional_after( + loc_expr_block(true), + EExpr::IndentStart, + EExpr::IndentEnd, + ), + expr_end(), + ); + + match parser.parse(&self.arena, state, 0) { + Ok((_, loc_expr, _)) => Ok(loc_expr), + Err((_, fail)) => Err(SourceError { + problem: SyntaxError::Expr(fail, Position::default()), + bytes: original_bytes, + }), + } + } + + pub fn into_arena(self) -> Bump { + self.arena + } + + pub fn arena(&self) -> &Bump { + &self.arena + } +} diff --git a/crates/test_compile/src/help_solve.rs b/crates/test_compile/src/help_solve.rs new file mode 100644 index 0000000000..7002e36534 --- /dev/null +++ b/crates/test_compile/src/help_solve.rs @@ -0,0 +1,85 @@ +use crate::help_constrain::ConstrainedExpr; +use bumpalo::Bump; +use roc_can::{ + abilities::AbilitiesStore, + expr::{Expr, PendingDerives}, + module::ExposedByModule, +}; +use roc_collections::VecMap; +use roc_derive::SharedDerivedModule; +use roc_load::FunctionKind; +use roc_module::symbol::ModuleId; +use roc_region::all::Region; +use roc_solve::{ + module::{SolveConfig, Solved}, + solve, Aliases, +}; +use roc_solve_problem::TypeError; +use roc_types::subs::{Subs, Variable}; + +#[derive(Debug)] +pub struct SolvedExprOut { + pub expr: Expr, + pub region: Region, + pub problems: Vec, + pub var: Variable, + pub subs: Solved, +} + +#[derive(Default)] +pub struct SolvedExpr { + constrained_expr: ConstrainedExpr, +} + +impl SolvedExpr { + pub fn solve_expr<'a>(&'a self, input: &'a str) -> SolvedExprOut { + let constrained_expr_out = self.constrained_expr.constrain_expr(input); + let mut problems = Vec::new(); + let mut aliases = Aliases::default(); + let subs = Subs::new(); + let mut abilities_store = AbilitiesStore::default(); + let solve_config = SolveConfig { + home: self.constrained_expr.home(), + constraints: &constrained_expr_out.constraints, + root_constraint: constrained_expr_out.constraint, + types: constrained_expr_out.types, + function_kind: FunctionKind::LambdaSet, + pending_derives: PendingDerives::default(), + exposed_by_module: &ExposedByModule::default(), + derived_module: SharedDerivedModule::default(), + module_params: None, + module_params_vars: VecMap::default(), + host_exposed_symbols: None, + #[cfg(debug_assertions)] + checkmate: None, + }; + + let solve_output = solve::run( + solve_config, + &mut problems, + subs, + &mut aliases, + &mut abilities_store, + ); + + SolvedExprOut { + expr: constrained_expr_out.expr, + region: constrained_expr_out.region, + problems, + var: constrained_expr_out.var, + subs: solve_output.solved, + } + } + + pub fn into_arena(self) -> Bump { + self.constrained_expr.into_arena() + } + + pub fn arena(&self) -> &Bump { + self.constrained_expr.arena() + } + + pub fn home(&self) -> ModuleId { + self.constrained_expr.home() + } +} diff --git a/crates/test_compile/src/help_specialize.rs b/crates/test_compile/src/help_specialize.rs new file mode 100644 index 0000000000..b750ec7cc4 --- /dev/null +++ b/crates/test_compile/src/help_specialize.rs @@ -0,0 +1,70 @@ +use crate::SolvedExpr; +use bumpalo::Bump; +use roc_region::all::Region; +use roc_specialize_types::{ + DebugInfo, Env, Interns, MonoExprId, MonoExprs, MonoTypeCache, MonoTypes, Problem, + RecordFieldIds, TupleElemIds, +}; + +#[derive(Debug)] +pub struct SpecializedExprOut { + pub mono_expr_id: Option, + pub region: Region, + pub mono_types: MonoTypes, + pub mono_exprs: MonoExprs, + pub problems: Vec, +} + +#[derive(Default)] +pub struct SpecializedExpr { + solved_expr: SolvedExpr, +} + +impl SpecializedExpr { + pub fn specialize_expr<'a>( + &'a self, + input: &'a str, + string_interns: &'a mut Interns<'a>, + ) -> SpecializedExprOut { + let mut solved_out = self.solved_expr.solve_expr(input); + let mut problems = Vec::new(); + let mut debug_info: Option = None; + let mut types_cache = MonoTypeCache::from_solved_subs(&solved_out.subs); + let mut mono_types = MonoTypes::new(); + let mut mono_exprs = MonoExprs::new(); + + let mono_expr_id = { + let mut env = Env::new( + self.solved_expr.arena(), + &mut solved_out.subs, + &mut types_cache, + &mut mono_types, + &mut mono_exprs, + RecordFieldIds::default(), + TupleElemIds::default(), + string_interns, + &mut debug_info, + &mut problems, + ); + + env.to_mono_expr(&solved_out.expr) + .map(|mono_expr| mono_exprs.add(mono_expr, Region::zero())) + }; + + SpecializedExprOut { + mono_expr_id, + region: solved_out.region, + problems, + mono_types, + mono_exprs, + } + } + + pub fn into_arena(self) -> Bump { + self.solved_expr.into_arena() + } + + pub fn arena(&self) -> &Bump { + self.solved_expr.arena() + } +} diff --git a/crates/test_compile/src/lib.rs b/crates/test_compile/src/lib.rs new file mode 100644 index 0000000000..724e753f81 --- /dev/null +++ b/crates/test_compile/src/lib.rs @@ -0,0 +1,23 @@ +// This crate is only used in tests, so panic is fine +#![allow(clippy::panic)] + +mod deindent; +mod help_can; +mod help_constrain; +mod help_parse; +mod help_solve; +mod help_specialize; + +pub use deindent::trim_and_deindent; +pub use help_can::{CanExpr, CanExprOut}; +pub use help_parse::ParseExpr; +pub use help_solve::{SolvedExpr, SolvedExprOut}; +pub use help_specialize::{SpecializedExpr, SpecializedExprOut}; + +pub fn can_expr(input: &str) -> CanExprOut { + CanExpr::default().can_expr(input) +} + +pub fn solve_expr(input: &str) -> SolvedExprOut { + SolvedExpr::default().solve_expr(input) +} diff --git a/www/content/docs.md b/www/content/docs.md index 1dd26aff1e..4e2b4b1ef9 100644 --- a/www/content/docs.md +++ b/www/content/docs.md @@ -2,7 +2,7 @@ - [builtins](/builtins) - docs for modules built into the language—`Str`, `Num`, etc. - [basic-webserver](https://roc-lang.github.io/basic-webserver/) - a platform for making Web servers ([source code](https://github.com/roc-lang/basic-webserver)) -- [basic-cli](/packages/basic-cli/0.15.0) - a platform for making command-line interfaces ([source code](https://github.com/roc-lang/basic-cli)) +- [basic-cli](/packages/basic-cli/0.16.0) - a platform for making command-line interfaces ([source code](https://github.com/roc-lang/basic-cli)) - [plans](/plans) - current plans for future changes to the language In the future, a language reference will be on this page too.