Merge branch 'roc-lang:main' into int_overflow

This commit is contained in:
wizard7377 2024-11-28 08:35:17 -05:00 committed by GitHub
commit 62db49f7d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
231 changed files with 5522 additions and 834 deletions

View file

@ -18,13 +18,23 @@ jobs:
run: nix-build run: nix-build
- name: execute tests with --release - 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 - name: roc test all builtins
run: nix develop -c ./ci/roc_test_builtins.sh run: nix develop -c ./ci/roc_test_builtins.sh
- name: test wasm32 cli_tests - 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 - 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 run: nix develop -c cargo nextest run --locked --release --package test_gen --no-default-features --features gen-dev --no-fail-fast

1
.gitignore vendored
View file

@ -59,6 +59,7 @@ metadata
#editors #editors
.idea/ .idea/
.vscode/ .vscode/
.helix/
.ignore .ignore
.exrc .exrc
.vimrc .vimrc

57
Cargo.lock generated
View file

@ -2354,6 +2354,7 @@ dependencies = [
"roc_types", "roc_types",
"soa", "soa",
"static_assertions", "static_assertions",
"test_compile",
"ven_pretty", "ven_pretty",
] ]
@ -2630,7 +2631,6 @@ dependencies = [
"cli_test_utils", "cli_test_utils",
"dircpy", "dircpy",
"fnv", "fnv",
"indexmap",
"indoc", "indoc",
"libc", "libc",
"libloading", "libloading",
@ -2954,7 +2954,6 @@ dependencies = [
"rustyline-derive", "rustyline-derive",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
"unicode-segmentation",
] ]
[[package]] [[package]]
@ -3140,6 +3139,36 @@ dependencies = [
"bitflags 1.3.2", "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]] [[package]]
name = "roc_std" name = "roc_std"
version = "0.0.1" version = "0.0.1"
@ -3781,6 +3810,30 @@ dependencies = [
"winapi-util", "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]] [[package]]
name = "test_derive" name = "test_derive"
version = "0.0.1" version = "0.0.1"

View file

@ -17,6 +17,7 @@ members = [
"crates/repl_wasm", "crates/repl_wasm",
"crates/repl_expect", "crates/repl_expect",
"crates/roc_std", "crates/roc_std",
"crates/test_compile",
"crates/test_utils", "crates/test_utils",
"crates/test_utils_dir", "crates/test_utils_dir",
"crates/valgrind_tests", "crates/valgrind_tests",
@ -75,11 +76,9 @@ version = "0.0.1"
inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [ inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [
"llvm16-0", "llvm16-0",
] } ] }
soa = { path = "crates/soa" } soa = { path = "crates/soa" }
roc_specialize_types = { path = "crates/compiler/specialize_types" }
arrayvec = "0.7.2" # update roc_std/Cargo.toml on change arrayvec = "0.7.2" # update roc_std/Cargo.toml on change
backtrace = "0.3.67"
base64-url = "1.4.13" base64-url = "1.4.13"
bincode = "1.3.3" bincode = "1.3.3"
bitflags = "1.3.2" bitflags = "1.3.2"
@ -87,9 +86,7 @@ bitvec = "1.0.1"
blake3 = "1.3.3" blake3 = "1.3.3"
brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli
bumpalo = { version = "3.12.0", features = ["collections"] } bumpalo = { version = "3.12.0", features = ["collections"] }
bytemuck = { version = "1.13.1", features = ["derive"] }
capstone = { version = "0.11.0", default-features = false } capstone = { version = "0.11.0", default-features = false }
cgmath = "0.18.0"
chrono = "0.4.26" chrono = "0.4.26"
clap = { version = "4.2.7", default-features = false, features = [ clap = { version = "4.2.7", default-features = false, features = [
"std", "std",
@ -99,10 +96,8 @@ clap = { version = "4.2.7", default-features = false, features = [
"usage", "usage",
"error-context", "error-context",
] } ] }
colored = "2.0.0"
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1.7"
const_format = { version = "0.2.30", features = ["const_generics"] } const_format = { version = "0.2.30", features = ["const_generics"] }
copypasta = "0.8.2"
criterion = { git = "https://github.com/Anton-4/criterion.rs", features = [ criterion = { git = "https://github.com/Anton-4/criterion.rs", features = [
"html_reports", "html_reports",
], rev = "30ea0c5" } ], rev = "30ea0c5" }
@ -116,7 +111,6 @@ flate2 = "1.0.25"
fnv = "1.0.7" fnv = "1.0.7"
fs_extra = "1.3.0" fs_extra = "1.3.0"
futures = "0.3.26" futures = "0.3.26"
glyph_brush = "0.7.7"
hashbrown = { version = "0.14.3" } hashbrown = { version = "0.14.3" }
iced-x86 = { version = "1.18.0", default-features = false, features = [ iced-x86 = { version = "1.18.0", default-features = false, features = [
"std", "std",
@ -126,7 +120,6 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [
] } ] }
im = "15.1.0" im = "15.1.0"
im-rc = "15.1.0" im-rc = "15.1.0"
indexmap = "2.1.0"
indoc = "1.0.9" indoc = "1.0.9"
insta = "1.28.0" insta = "1.28.0"
js-sys = "0.3.61" js-sys = "0.3.61"
@ -140,18 +133,13 @@ mach_object = "0.1"
maplit = "1.0.2" maplit = "1.0.2"
memmap2 = "0.5.10" memmap2 = "0.5.10"
mimalloc = { version = "0.1.34", default-features = false } mimalloc = { version = "0.1.34", default-features = false }
nonempty = "0.8.1"
object = { version = "0.32.2", default-features = false, features = [ object = { version = "0.32.2", default-features = false, features = [
"read", "read",
"write", "write",
] } ] }
packed_struct = "0.10.1" packed_struct = "0.10.1"
page_size = "0.5.0"
palette = "0.6.1"
parking_lot = "0.12" parking_lot = "0.12"
perfcnt = "0.8.0" 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 pretty_assertions = "1.3.0" # update roc_std/Cargo.toml on change
proc-macro2 = "1.0.63" proc-macro2 = "1.0.63"
proptest = "1.1.0" proptest = "1.1.0"
@ -185,21 +173,17 @@ syn = { version = "1.0.109", features = ["full", "extra-traits"] }
tar = "0.4.38" tar = "0.4.38"
target-lexicon = "0.12.6" target-lexicon = "0.12.6"
tempfile = "=3.2.0" tempfile = "=3.2.0"
threadpool = "1.8.1"
tracing = { version = "0.1.40", features = ["release_max_level_off"] } tracing = { version = "0.1.40", features = ["release_max_level_off"] }
tracing-appender = "0.2.2" tracing-appender = "0.2.2"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } 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" walkdir = "2.3.2"
wasm-bindgen = "0.2.84" wasm-bindgen = "0.2.84"
wasm-bindgen-futures = "0.4.34" 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" wyhash = "0.5.0"
# Testing
test_compile = { path = "crates/test_compile" }
# Optimizations based on https://deterministic.space/high-performance-rust.html # Optimizations based on https://deterministic.space/high-performance-rust.html
[profile.release] [profile.release]
codegen-units = 1 codegen-units = 1

View file

@ -30,3 +30,4 @@ soa.workspace = true
indoc.workspace = true indoc.workspace = true
insta.workspace = true insta.workspace = true
pretty_assertions.workspace = true pretty_assertions.workspace = true
test_compile.workspace = true

View file

@ -132,7 +132,7 @@ pub struct AbleVariable {
pub first_seen: Region, pub first_seen: Region,
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default, PartialEq)]
pub struct IntroducedVariables { pub struct IntroducedVariables {
pub wildcards: Vec<Loc<Variable>>, pub wildcards: Vec<Loc<Variable>>,
pub lambda_sets: Vec<Variable>, pub lambda_sets: Vec<Variable>,

View file

@ -929,7 +929,7 @@ pub struct FxExpectation {
pub ann_region: Option<Region>, pub ann_region: Option<Region>,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum FxCallKind { pub enum FxCallKind {
Call(Option<Symbol>), Call(Option<Symbol>),
Stmt, Stmt,
@ -943,7 +943,7 @@ pub struct FxSuffixConstraint {
pub region: Region, pub region: Region,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum FxSuffixKind { pub enum FxSuffixKind {
Let(Symbol), Let(Symbol),
Pattern(Symbol), Pattern(Symbol),
@ -957,9 +957,16 @@ impl FxSuffixKind {
Self::UnsuffixedRecordField => IdentSuffix::None, 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 { pub enum ExpectEffectfulReason {
Stmt, Stmt,
Ignored, Ignored,

View file

@ -63,7 +63,7 @@ use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct Def { pub struct Def {
pub loc_pattern: Loc<Pattern>, pub loc_pattern: Loc<Pattern>,
pub loc_expr: Loc<Expr>, pub loc_expr: Loc<Expr>,
@ -91,7 +91,7 @@ impl Def {
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum DefKind { pub enum DefKind {
/// A def that introduces identifiers /// A def that introduces identifiers
Let, Let,
@ -123,7 +123,7 @@ impl DefKind {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct Annotation { pub struct Annotation {
pub signature: Type, pub signature: Type,
pub introduced_variables: IntroducedVariables, pub introduced_variables: IntroducedVariables,

View file

@ -2,7 +2,7 @@ use crate::pattern::Pattern;
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
use roc_types::types::{AnnotationSource, PReason, Reason}; use roc_types::types::{AnnotationSource, PReason, Reason};
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum Expected<T> { pub enum Expected<T> {
NoExpectation(T), NoExpectation(T),
FromAnnotation(Loc<Pattern>, usize, AnnotationSource, T), FromAnnotation(Loc<Pattern>, usize, AnnotationSource, T),
@ -10,7 +10,7 @@ pub enum Expected<T> {
} }
/// Like Expected, but for Patterns. /// Like Expected, but for Patterns.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum PExpected<T> { pub enum PExpected<T> {
NoExpectation(T), NoExpectation(T),
ForReason(PReason, T, Region), ForReason(PReason, T, Region),

View file

@ -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 { pub enum Expr {
// Literals // Literals
@ -104,7 +152,7 @@ pub enum Expr {
loc_elems: Vec<Loc<Expr>>, loc_elems: Vec<Loc<Expr>>,
}, },
// An ingested files, it's bytes, and the type variable. // An ingested files, its bytes, and the type variable.
IngestedFile(Box<PathBuf>, Arc<Vec<u8>>, Variable), IngestedFile(Box<PathBuf>, Arc<Vec<u8>>, Variable),
// Lookups // Lookups
@ -131,7 +179,7 @@ pub enum Expr {
/// The actual condition of the when expression. /// The actual condition of the when expression.
loc_cond: Box<Loc<Expr>>, loc_cond: Box<Loc<Expr>>,
cond_var: Variable, 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, expr_var: Variable,
region: Region, region: Region,
/// The branches of the when, and the type of the condition that they expect to be matched /// 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), RuntimeError(RuntimeError),
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct ExpectLookup { pub struct ExpectLookup {
pub symbol: Symbol, pub symbol: Symbol,
pub var: Variable, pub var: Variable,
@ -370,7 +418,7 @@ impl Expr {
/// Stores exhaustiveness-checking metadata for a closure argument that may /// Stores exhaustiveness-checking metadata for a closure argument that may
/// have an annotated type. /// have an annotated type.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct AnnotatedMark { pub struct AnnotatedMark {
pub annotation_var: Variable, pub annotation_var: Variable,
pub exhaustive: ExhaustiveMark, pub exhaustive: ExhaustiveMark,
@ -394,7 +442,7 @@ impl AnnotatedMark {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct ClosureData { pub struct ClosureData {
pub function_type: Variable, pub function_type: Variable,
pub closure_type: Variable, pub closure_type: Variable,
@ -491,7 +539,7 @@ impl StructAccessorData {
/// An opaque wrapper like `@Foo`, which is equivalent to `\p -> @Foo p` /// 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 /// These are desugared to closures, but we distinguish them so we can have
/// better error messages during constraint generation. /// better error messages during constraint generation.
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct OpaqueWrapFunctionData { pub struct OpaqueWrapFunctionData {
pub opaque_name: Symbol, pub opaque_name: Symbol,
pub opaque_var: Variable, pub opaque_var: Variable,
@ -563,7 +611,7 @@ impl OpaqueWrapFunctionData {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct Field { pub struct Field {
pub var: Variable, pub var: Variable,
// The region of the full `foo: f bar`, rather than just `f bar` // 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 struct WhenBranchPattern {
pub pattern: Loc<Pattern>, pub pattern: Loc<Pattern>,
/// Degenerate branch patterns are those that don't fully bind symbols that the branch body /// 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, pub degenerate: bool,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct WhenBranch { pub struct WhenBranch {
pub patterns: Vec<WhenBranchPattern>, pub patterns: Vec<WhenBranchPattern>,
pub value: Loc<Expr>, pub value: Loc<Expr>,

View file

@ -19,7 +19,7 @@ use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
/// A pattern, including possible problems (e.g. shadowing) so that /// A pattern, including possible problems (e.g. shadowing) so that
/// codegen can generate a runtime error if this pattern is reached. /// codegen can generate a runtime error if this pattern is reached.
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub enum Pattern { pub enum Pattern {
Identifier(Symbol), Identifier(Symbol),
As(Box<Loc<Pattern>>, Symbol), As(Box<Loc<Pattern>>, Symbol),
@ -198,7 +198,7 @@ impl Pattern {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct ListPatterns { pub struct ListPatterns {
pub patterns: Vec<Loc<Pattern>>, pub patterns: Vec<Loc<Pattern>>,
/// Where a rest pattern splits patterns before and after it, if it does at all. /// 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 = 0
/// [ A, .., B ] -> patterns = [A, B], rest = 1 /// [ A, .., B ] -> patterns = [A, B], rest = 1
/// [ A, B, .. ] -> patterns = [A, B], rest = 2 /// [ 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<Loc<Symbol>>)>, pub opt_rest: Option<(usize, Option<Loc<Symbol>>)>,
} }
@ -228,7 +229,7 @@ impl ListPatterns {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct RecordDestruct { pub struct RecordDestruct {
pub var: Variable, pub var: Variable,
pub label: Lowercase, pub label: Lowercase,
@ -236,14 +237,14 @@ pub struct RecordDestruct {
pub typ: DestructType, pub typ: DestructType,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub struct TupleDestruct { pub struct TupleDestruct {
pub var: Variable, pub var: Variable,
pub destruct_index: usize, pub destruct_index: usize,
pub typ: (Variable, Loc<Pattern>), pub typ: (Variable, Loc<Pattern>),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, PartialEq)]
pub enum DestructType { pub enum DestructType {
Required, Required,
Optional(Variable, Loc<Expr>), Optional(Variable, Loc<Expr>),

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-28, @0-28,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -65,10 +66,10 @@ Defs {
@15-22, @15-22,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-25, @0-25,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -49,10 +50,10 @@ Defs {
@15-19, @15-19,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-43, @0-43,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -59,10 +60,10 @@ Defs {
@15-33, @15-33,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-45, @0-45,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -59,10 +60,10 @@ Defs {
@15-35, @15-35,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-28, @0-28,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -49,10 +50,10 @@ Defs {
@15-22, @15-22,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-26, @0-26,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -39,10 +40,10 @@ Defs {
@11-14, @11-14,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-42, @0-42,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -49,10 +50,10 @@ Defs {
@16-35, @16-35,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -11,10 +11,10 @@ Defs {
@0-69, @0-69,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -34,10 +34,10 @@ Defs {
@15-57, @15-57,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -67,10 +67,10 @@ Defs {
@31-43, @31-43,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-114, @0-114,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@39-101, @39-101,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -84,10 +85,10 @@ Defs {
@82-91, @82-91,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-143, @0-143,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@56-119, @56-119,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -112,10 +113,10 @@ Defs {
@76-83, @76-83,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -181,10 +182,10 @@ Defs {
@92-99, @92-99,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,19 +1,20 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
EitherIndex(2147483648), EitherIndex(2147483648),
], ],
regions: [ regions: [
@0-26, @0-28,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -24,88 +25,90 @@ Defs {
@0-4 Identifier { @0-4 Identifier {
ident: "main", ident: "main",
}, },
@11-26 Defs( @11-28 Defs(
Defs { Defs {
tags: [ tags: [
EitherIndex(2147483648), EitherIndex(2147483648),
], ],
regions: [ regions: [
@15-26, @16-27,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@15-26 Identifier { @16-27 Identifier {
ident: "1", ident: "1",
}, },
@15-26 ParensAround( @16-27 ParensAround(
Defs( Defs(
Defs { Defs {
tags: [ tags: [
EitherIndex(2147483648), EitherIndex(2147483648),
], ],
regions: [ regions: [
@20-25, @21-26,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
value_defs: [ value_defs: [
Body( Body(
@20-25 Identifier { @21-26 Identifier {
ident: "0", ident: "0",
}, },
@20-25 Apply( @21-26 ParensAround(
@22-23 Var { Apply(
module_name: "Num", @23-24 Var {
ident: "add", module_name: "Num",
}, ident: "add",
[ },
@20-21 Num( [
"1", @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", "test.roc:3",
" ", " ",
), ),
@20-25 Apply( @21-26 Apply(
@20-25 Var { @21-26 Var {
module_name: "Inspect", module_name: "Inspect",
ident: "toStr", ident: "toStr",
}, },
[ [
@20-25 Var { @21-26 Var {
module_name: "", module_name: "",
ident: "0", ident: "0",
}, },
], ],
Space, Space,
), ),
@20-25 Var { @21-26 Var {
module_name: "", module_name: "",
ident: "0", ident: "0",
}, },
@ -115,25 +118,25 @@ Defs {
), ),
], ],
}, },
@11-26 LowLevelDbg( @11-28 LowLevelDbg(
( (
"test.roc:2", "test.roc:2",
"in =\n ", "n =\n ",
), ),
@15-26 Apply( @16-27 Apply(
@15-26 Var { @16-27 Var {
module_name: "Inspect", module_name: "Inspect",
ident: "toStr", ident: "toStr",
}, },
[ [
@15-26 Var { @16-27 Var {
module_name: "", module_name: "",
ident: "1", ident: "1",
}, },
], ],
Space, Space,
), ),
@15-26 Var { @16-27 Var {
module_name: "", module_name: "",
ident: "1", ident: "1",
}, },

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-49, @0-49,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-24, @0-24,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-99, @0-99,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -12,12 +13,12 @@ Defs {
@56-98, @56-98,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 2 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 2, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -39,10 +40,10 @@ Defs {
@15-20, @15-20,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -74,10 +75,10 @@ Defs {
@25-39, @25-39,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -151,10 +152,10 @@ Defs {
@75-80, @75-80,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-158, @0-158,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -45,10 +46,10 @@ Defs {
@50-52, @50-52,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-31, @0-31,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@11-24, @11-24,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-307, @0-307,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -37,14 +38,14 @@ Defs {
@109-298, @109-298,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
Slice { start: 1, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 1 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 1, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
Slice { start: 2, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -171,10 +172,10 @@ Defs {
@140-152, @140-152,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -293,10 +294,10 @@ Defs {
@227-239, @227-239,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-189, @0-189,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -35,12 +36,12 @@ Defs {
@52-70, @52-70,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 1, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -14,14 +15,14 @@ Defs {
@229-266, @229-266,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 2 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
Slice { start: 2, length: 2 }, Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 2 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 2, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
Slice { start: 4, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 4, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -120,12 +121,12 @@ Defs {
@203-208, @203-208,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 1, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -12,12 +13,12 @@ Defs {
@35-45, @35-45,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 2 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 2, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-26, @0-26,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@15-17, @15-17,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-33, @0-33,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -39,10 +40,10 @@ Defs {
@11-14, @11-14,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -99,10 +100,10 @@ Defs {
@20-23, @20-23,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-26, @0-26,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -11,10 +11,10 @@ Defs {
@0-72, @0-72,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -40,10 +40,10 @@ Defs {
@11-23, @11-23,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-51, @0-51,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@17-24, @17-24,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-24, @0-24,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -39,10 +40,10 @@ Defs {
@11-16, @11-16,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-61, @0-61,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-49, @0-49,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@23-42, @23-42,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-22, @0-22,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-51, @0-51,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -33,10 +34,10 @@ Defs {
@11-40, @11-40,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],
@ -60,10 +61,10 @@ Defs {
@11-12, @11-12,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -11,10 +11,10 @@ Defs {
@0-73, @0-73,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -40,10 +40,10 @@ Defs {
@11-57, @11-57,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-67, @0-67,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -48,10 +49,10 @@ Defs {
@19-30, @19-30,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-154, @0-154,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-44, @0-44,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -45,10 +46,10 @@ Defs {
@28-29, @28-29,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-45, @0-45,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-120, @0-120,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,
@ -67,10 +68,10 @@ Defs {
@54-65, @54-65,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -1,6 +1,7 @@
--- ---
source: crates/compiler/can/tests/test_suffixed.rs source: crates/compiler/can/tests/test_suffixed.rs
expression: snapshot expression: snapshot
snapshot_kind: text
--- ---
Defs { Defs {
tags: [ tags: [
@ -10,10 +11,10 @@ Defs {
@0-74, @0-74,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 1 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -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));
}
}

View file

@ -464,7 +464,7 @@ mod suffixed_tests {
run_test!( run_test!(
r#" r#"
main = main =
dbg (dbg 1 + 1) dbg (dbg (1 + 1))
"# "#
); );
} }

View file

@ -4,6 +4,7 @@
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
pub mod all; pub mod all;
mod push;
mod reference_matrix; mod reference_matrix;
mod small_string_interner; mod small_string_interner;
mod small_vec; mod small_vec;
@ -12,6 +13,7 @@ mod vec_map;
mod vec_set; mod vec_set;
pub use all::{default_hasher, BumpMap, ImEntry, ImMap, ImSet, MutMap, MutSet, SendMap}; 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 reference_matrix::{ReferenceMatrix, Sccs, TopologicalSort};
pub use small_string_interner::SmallStringInterner; pub use small_string_interner::SmallStringInterner;
pub use small_vec::SmallVec; pub use small_vec::SmallVec;

View file

@ -0,0 +1,15 @@
pub trait Push<T> {
fn push(&mut self, entry: T);
}
impl<T> Push<T> for Vec<T> {
fn push(&mut self, entry: T) {
self.push(entry);
}
}
impl<T> Push<T> for &mut Vec<T> {
fn push(&mut self, entry: T) {
(*self).push(entry);
}
}

View file

@ -64,6 +64,15 @@ pub struct Env {
} }
impl 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<F, T>( pub fn with_fx_expectation<F, T>(
&mut self, &mut self,
fx_var: Variable, fx_var: Variable,

View file

@ -447,7 +447,7 @@ impl<'a> Formattable for Expr<'a> {
buf.push_str("dbg"); buf.push_str("dbg");
} }
DbgStmt(condition, continuation) => { DbgStmt(condition, continuation) => {
fmt_dbg_stmt(buf, condition, continuation, self.is_multiline(), indent); fmt_dbg_stmt(buf, condition, continuation, parens, indent);
} }
LowLevelDbg(_, _, _) => unreachable!( LowLevelDbg(_, _, _) => unreachable!(
"LowLevelDbg should only exist after desugaring, not during formatting" "LowLevelDbg should only exist after desugaring, not during formatting"
@ -1022,42 +1022,15 @@ fn fmt_dbg_stmt<'a>(
buf: &mut Buf, buf: &mut Buf,
condition: &'a Loc<Expr<'a>>, condition: &'a Loc<Expr<'a>>,
continuation: &'a Loc<Expr<'a>>, continuation: &'a Loc<Expr<'a>>,
_: bool, parens: Parens,
indent: u16, indent: u16,
) { ) {
buf.ensure_ends_with_newline(); Expr::Apply(
buf.indent(indent); &Loc::at_zero(Expr::Dbg),
buf.push_str("dbg"); &[condition],
called_via::CalledVia::Space,
buf.spaces(1); )
.format_with_options(buf, parens, Newlines::Yes, indent);
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);
// Always put a blank line after the `dbg` line(s) // Always put a blank line after the `dbg` line(s)
buf.ensure_ends_with_blank_line(); buf.ensure_ends_with_blank_line();

View file

@ -1846,6 +1846,26 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
neg_reg64_reg64(buf, dst, src); 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)] #[inline(always)]
fn sub_reg64_reg64_imm32( fn sub_reg64_reg64_imm32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
@ -3953,6 +3973,24 @@ fn fsub_freg_freg_freg(
buf.extend(inst.bytes()); 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. /// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags.
#[inline(always)] #[inline(always)]
fn fcmp_freg_freg( fn fcmp_freg_freg(

View file

@ -557,6 +557,18 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
fn sqrt_freg32_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg); 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_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( fn mul_freg32_freg32_freg32(
buf: &mut Vec<'_, u8>, buf: &mut Vec<'_, u8>,
dst: FloatReg, dst: FloatReg,
@ -1791,7 +1803,24 @@ impl<
let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src); 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); 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),
} }
} }

View file

@ -2602,6 +2602,28 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
neg_reg64(buf, dst); 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)] #[inline(always)]
fn sub_reg64_reg64_imm32( fn sub_reg64_reg64_imm32(
buf: &mut Vec<'_, u8>, 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. /// `TEST r/m64,r64` -> AND r64 with r/m64; set SF, ZF, PF according to result.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]

View file

@ -2241,6 +2241,7 @@ fn build_dec_unary_op<'a, 'ctx>(
match op { match op {
NumAbs => dec_unary_op(env, bitcode::DEC_ABS, arg), 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), NumAcos => dec_unary_op(env, bitcode::DEC_ACOS, arg),
NumAsin => dec_unary_op(env, bitcode::DEC_ASIN, arg), NumAsin => dec_unary_op(env, bitcode::DEC_ASIN, arg),
NumAtan => dec_unary_op(env, bitcode::DEC_ATAN, arg), NumAtan => dec_unary_op(env, bitcode::DEC_ATAN, arg),

View file

@ -1624,6 +1624,7 @@ impl<'a> LowLevelCall<'a> {
} }
F32 => backend.code_builder.f32_neg(), F32 => backend.code_builder.f32_neg(),
F64 => backend.code_builder.f64_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), _ => todo!("{:?} for {:?}", self.lowlevel, self.ret_layout),
} }
} }

View file

@ -53,6 +53,7 @@ pub fn infer_expr(
function_kind: FunctionKind::LambdaSet, function_kind: FunctionKind::LambdaSet,
module_params: None, module_params: None,
module_params_vars: Default::default(), module_params_vars: Default::default(),
host_exposed_symbols: None,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: None, checkmate: None,
}; };
@ -189,6 +190,24 @@ pub fn can_expr_with<'a>(
// rules multiple times unnecessarily. // rules multiple times unnecessarily.
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr); 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( let (loc_expr, output) = canonicalize_expr(
&mut env, &mut env,
&mut var_store, &mut var_store,

View file

@ -11054,8 +11054,8 @@ All branches in an `if` must have the same type!
4 Recursive := [Infinitely Recursive] 4 Recursive := [Infinitely Recursive]
^^^^^^^^^ ^^^^^^^^^
Recursion in opaquees is only allowed if recursion happens behind a Recursion in opaque types is only allowed if recursion happens behind
tagged union, at least one variant of which is not recursive. 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 Str.trim msg
"# "#
), ),
@r###" @r#"
EFFECT IN PURE FUNCTION in /code/proj/Main.roc EFFECT IN PURE FUNCTION in /code/proj/Main.roc
This call to `Effect.putLine!` might produce an effect: 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 You can still run the program with this error, which can be helpful
when you're debugging. 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!( test_report!(
@ -15423,7 +15412,7 @@ All branches in an `if` must have the same type!
pureHigherOrder = \f, x -> f x pureHigherOrder = \f, x -> f x
"# "#
), ),
@r###" @r#"
TYPE MISMATCH in /code/proj/Main.roc TYPE MISMATCH in /code/proj/Main.roc
This 1st argument to `pureHigherOrder` has an unexpected type: 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: But `pureHigherOrder` needs its 1st argument to be:
Str -> {} 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!( test_report!(
@ -15467,7 +15445,7 @@ All branches in an `if` must have the same type!
pureHigherOrder = \f, x -> f x pureHigherOrder = \f, x -> f x
"# "#
), ),
@r###" @r#"
TYPE MISMATCH in /code/proj/Main.roc TYPE MISMATCH in /code/proj/Main.roc
This 1st argument to `pureHigherOrder` has an unexpected type: 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: But `pureHigherOrder` needs its 1st argument to be:
Str -> {} 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?
"###
); );
} }

View file

@ -350,6 +350,8 @@ fn start_phase<'a>(
None None
}; };
let is_host_exposed = state.root_id == module.module_id;
BuildTask::solve_module( BuildTask::solve_module(
module, module,
ident_ids, ident_ids,
@ -367,6 +369,7 @@ fn start_phase<'a>(
state.cached_types.clone(), state.cached_types.clone(),
derived_module, derived_module,
state.exec_mode, state.exec_mode,
is_host_exposed,
// //
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -922,6 +925,7 @@ enum BuildTask<'a> {
cached_subs: CachedTypeState, cached_subs: CachedTypeState,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
exec_mode: ExecutionMode, exec_mode: ExecutionMode,
is_host_exposed: bool,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: Option<roc_checkmate::Collector>, checkmate: Option<roc_checkmate::Collector>,
@ -4331,6 +4335,7 @@ impl<'a> BuildTask<'a> {
cached_subs: CachedTypeState, cached_subs: CachedTypeState,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
exec_mode: ExecutionMode, exec_mode: ExecutionMode,
is_host_exposed: bool,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>, #[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> Self { ) -> Self {
@ -4355,6 +4360,7 @@ impl<'a> BuildTask<'a> {
cached_subs, cached_subs,
derived_module, derived_module,
exec_mode, exec_mode,
is_host_exposed,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -4661,6 +4667,7 @@ fn run_solve_solve(
var_store: VarStore, var_store: VarStore,
module: Module, module: Module,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
is_host_exposed: bool,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>, #[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> SolveResult { ) -> SolveResult {
@ -4711,6 +4718,12 @@ fn run_solve_solve(
let (solve_output, solved_implementations, exposed_vars_by_symbol) = { let (solve_output, solved_implementations, exposed_vars_by_symbol) = {
let module_id = module.module_id; let module_id = module.module_id;
let host_exposed_idents = if is_host_exposed {
Some(&exposed_symbols)
} else {
None
};
let solve_config = SolveConfig { let solve_config = SolveConfig {
home: module_id, home: module_id,
types, types,
@ -4724,6 +4737,7 @@ fn run_solve_solve(
checkmate, checkmate,
module_params, module_params,
module_params_vars: imported_param_vars, module_params_vars: imported_param_vars,
host_exposed_symbols: host_exposed_idents,
}; };
let solve_output = roc_solve::module::run_solve( let solve_output = roc_solve::module::run_solve(
@ -4800,6 +4814,7 @@ fn run_solve<'a>(
cached_types: CachedTypeState, cached_types: CachedTypeState,
derived_module: SharedDerivedModule, derived_module: SharedDerivedModule,
exec_mode: ExecutionMode, exec_mode: ExecutionMode,
is_host_exposed: bool,
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>, #[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
) -> Msg<'a> { ) -> Msg<'a> {
@ -4831,6 +4846,7 @@ fn run_solve<'a>(
var_store, var_store,
module, module,
derived_module, derived_module,
is_host_exposed,
// //
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -4863,6 +4879,7 @@ fn run_solve<'a>(
var_store, var_store,
module, module,
derived_module, derived_module,
is_host_exposed,
// //
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -6256,6 +6273,7 @@ fn run_task<'a>(
cached_subs, cached_subs,
derived_module, derived_module,
exec_mode, exec_mode,
is_host_exposed,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,
@ -6275,6 +6293,7 @@ fn run_task<'a>(
cached_subs, cached_subs,
derived_module, derived_module,
exec_mode, exec_mode,
is_host_exposed,
// //
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate, checkmate,

View file

@ -35,7 +35,7 @@ use roc_region::all::{Loc, Position, Region};
use crate::parser::Progress::{self, *}; 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| { |_arena, state: State<'a>, _min_indent: u32| {
if state.has_reached_end() { if state.has_reached_end() {
Ok((NoProgress, (), state)) Ok((NoProgress, (), state))
@ -545,10 +545,6 @@ fn stmt_start<'a>(
EExpr::Expect, EExpr::Expect,
expect_help(options, preceding_comment) 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::Return, return_help(options))),
loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))), loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))),
map( map(
@ -2668,34 +2664,6 @@ fn return_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Stmt<'a>, ERetu
.trace("return_help") .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>> { fn dbg_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpect<'a>> {
(move |arena: &'a Bump, state: State<'a>, min_indent: u32| { (move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let (_, _, next_state) = let (_, _, next_state) =
@ -3110,12 +3078,43 @@ fn stmts_to_defs<'a>(
} }
Stmt::Expr(e) => { Stmt::Expr(e) => {
if i + 1 < stmts.len() { if i + 1 < stmts.len() {
defs.push_value_def( if let Expr::Apply(
ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))), Loc {
sp_stmt.item.region, value: Expr::Dbg, ..
sp_stmt.before, },
&[], 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 { } else {
let e = if sp_stmt.before.is_empty() { let e = if sp_stmt.before.is_empty() {
e e

View file

@ -1465,6 +1465,7 @@ impl<'a> Normalize<'a> for EExpect<'a> {
EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero()) EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero())
} }
EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()), EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()),
EExpect::DbgArity(_) => EExpect::DbgArity(Position::zero()),
} }
} }
} }

View file

@ -513,6 +513,7 @@ pub enum EExpect<'a> {
Condition(&'a EExpr<'a>, Position), Condition(&'a EExpr<'a>, Position),
Continuation(&'a EExpr<'a>, Position), Continuation(&'a EExpr<'a>, Position),
IndentCondition(Position), IndentCondition(Position),
DbgArity(Position),
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -6,7 +6,7 @@ use roc_can::constraint::{Constraint, Constraints};
use roc_can::expr::PendingDerives; use roc_can::expr::PendingDerives;
use roc_can::module::{ExposedByModule, ModuleParams, ResolvedImplementations, RigidVariables}; use roc_can::module::{ExposedByModule, ModuleParams, ResolvedImplementations, RigidVariables};
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_collections::VecMap; use roc_collections::{VecMap, VecSet};
use roc_derive::SharedDerivedModule; use roc_derive::SharedDerivedModule;
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_module::symbol::{ModuleId, Symbol}; 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 /// Needed during solving to resolve lambda sets from derived implementations that escape into
/// the user module. /// the user module.
pub derived_module: SharedDerivedModule, pub derived_module: SharedDerivedModule,
/// Symbols that are exposed to the host which might need special treatment.
pub host_exposed_symbols: Option<&'a VecSet<Symbol>>,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
/// The checkmate collector for this module. /// The checkmate collector for this module.

View file

@ -19,7 +19,7 @@ use roc_can::constraint::{
}; };
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_can::module::ModuleParams; use roc_can::module::ModuleParams;
use roc_collections::VecMap; use roc_collections::{VecMap, VecSet};
use roc_debug_flags::dbg_do; use roc_debug_flags::dbg_do;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED;
@ -136,6 +136,7 @@ fn run_help(
function_kind, function_kind,
module_params, module_params,
module_params_vars, module_params_vars,
host_exposed_symbols,
.. ..
} = config; } = config;
@ -190,6 +191,7 @@ fn run_help(
&mut awaiting_specializations, &mut awaiting_specializations,
module_params, module_params,
module_params_vars, module_params_vars,
host_exposed_symbols,
); );
RunSolveOutput { RunSolveOutput {
@ -249,6 +251,7 @@ fn solve(
awaiting_specializations: &mut AwaitingSpecializations, awaiting_specializations: &mut AwaitingSpecializations,
module_params: Option<ModuleParams>, module_params: Option<ModuleParams>,
module_params_vars: VecMap<ModuleId, Variable>, module_params_vars: VecMap<ModuleId, Variable>,
host_exposed_symbols: Option<&VecSet<Symbol>>,
) -> State { ) -> State {
let scope = Scope::new(module_params); let scope = Scope::new(module_params);
@ -455,6 +458,7 @@ fn solve(
solve_suffix_fx( solve_suffix_fx(
env, env,
problems, problems,
host_exposed_symbols,
FxSuffixKind::Let(*symbol), FxSuffixKind::Let(*symbol),
loc_var.value, loc_var.value,
&loc_var.region, &loc_var.region,
@ -853,7 +857,7 @@ fn solve(
*type_index, *type_index,
); );
solve_suffix_fx(env, problems, *kind, actual, region); solve_suffix_fx(env, problems, host_exposed_symbols, *kind, actual, region);
state state
} }
ExpectEffectful(variable, reason, region) => { ExpectEffectful(variable, reason, region) => {
@ -1625,6 +1629,7 @@ fn solve(
fn solve_suffix_fx( fn solve_suffix_fx(
env: &mut InferenceEnv<'_>, env: &mut InferenceEnv<'_>,
problems: &mut Vec<TypeError>, problems: &mut Vec<TypeError>,
host_exposed_symbols: Option<&VecSet<Symbol>>,
kind: FxSuffixKind, kind: FxSuffixKind,
variable: Variable, variable: Variable,
region: &Region, region: &Region,
@ -1651,7 +1656,16 @@ fn solve_suffix_fx(
let fx = *fx; let fx = *fx;
match env.subs.get_content_without_compacting(fx) { match env.subs.get_content_without_compacting(fx) {
Content::Pure => { 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(_) => { Content::FlexVar(_) => {
env.subs.set_content(fx, Content::Effectful); env.subs.set_content(fx, Content::Effectful);

View file

@ -15,7 +15,7 @@ use roc_region::all::Region;
use roc_types::types::{Category, ErrorType, PatternCategory}; use roc_types::types::{Category, ErrorType, PatternCategory};
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq)]
pub enum TypeError { pub enum TypeError {
BadExpr(Region, Category, ErrorType, Expected<ErrorType>), BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>), BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),

View file

@ -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

View file

@ -0,0 +1,8 @@
#[derive(Default)]
pub struct DebugInfo;
impl DebugInfo {
pub fn new() -> Self {
DebugInfo
}
}

View file

@ -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<Expr>, subs: &mut Subs) -> Loc<Expr> {
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<Variable> = 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<Variable> = 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<Variable> = 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<Variable> = 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<Variable> = 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<Variable> = 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<Variable> = 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<Variable> = 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,
}
}

View file

@ -0,0 +1,37 @@
use roc_module::ident::ForeignSymbol;
use soa::Index;
#[derive(Debug, Default)]
pub struct ForeignSymbols {
inner: Vec<ForeignSymbol>,
}
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<ForeignSymbol>,
}

View file

@ -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};

View file

@ -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<DebugInfo>,
string_interns: &'i mut Interns<'a>,
problems: P,
}
impl<'a, 'c, 'd, 'i, 's, 't, P: Push<Problem>> Env<'a, 'c, 'd, 'i, 's, 't, P> {
pub fn new(
arena: &'a Bump,
subs: &'s mut Solved<Subs>,
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<DebugInfo>,
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<MonoExpr> {
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<MonoExpr> 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<Pattern>,
// // pub loc_expr: Loc<Expr>,
// // pub expr_var: Variable,
// // pub pattern_vars: SendMap<Symbol, Variable>,
// // pub annotation: Option<Annotation>,
// // }
// 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<Problem>) -> 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<Problem>) -> 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<Problem>) -> 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<Problem>,
// ) -> 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
// }
// }
// }

View file

@ -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<IdentId, MonoTypeId>,
pub expr: MonoExprId,
pub expr_type: MonoTypeId,
}
#[derive(Debug, Default)]
pub struct MonoExprs {
// TODO convert to Vec2
exprs: Vec<MonoExpr>,
regions: Vec<Region>,
}
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<MonoExprId> {
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<MonoExpr>) -> impl Iterator<Item = &MonoExpr> {
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<Item = (MonoExpr, Region)> + Clone,
) -> Slice<MonoExpr> {
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<MonoExpr>,
}
impl MonoExprId {
pub(crate) unsafe fn new_unchecked(inner: Index<MonoExpr>) -> Self {
Self { inner }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MonoStmtId {
inner: Index<MonoStmt>,
}
impl MonoStmtId {
pub(crate) unsafe fn new_unchecked(inner: Index<MonoStmt>) -> 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<MonoTypeId, IdentId>,
},
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<MonoTypeId, MonoExprId>,
/// 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<WhenBranch>,
},
If {
/// Type of each branch (and therefore the type of the entire `if` expression)
branch_type: MonoTypeId,
branches: Slice<(MonoStmtId, MonoStmtId)>,
final_else: Option<MonoTypeId>,
},
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MonoExpr {
Str(InternedStrId),
Number(Number),
List {
elem_type: MonoTypeId,
elems: Slice<MonoExprId>,
},
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<MonoTypeId, MonoExprId>,
/// 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<MonoExpr>),
/// 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<MonoFieldId, MonoExprId>,
},
/// Same as BigTag but with u8 discriminant instead of u16
SmallTag {
discriminant: u8,
tag_union_type: MonoTypeId,
args: Slice2<MonoTypeId, MonoExprId>,
},
/// Same as SmallTag but with u16 discriminant instead of u8
BigTag {
discriminant: u16,
tag_union_type: MonoTypeId,
args: Slice2<MonoTypeId, MonoExprId>,
},
Block {
stmts: Slice<MonoStmtId>,
final_expr: MonoExprId,
},
CompilerBug(Problem),
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct WhenBranch {
pub patterns: Slice<MonoPatternId>,
pub body: Slice<MonoStmtId>,
pub guard: Option<MonoExprId>,
}
#[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<MonoPatternId>,
},
StructDestructure {
struct_type: MonoTypeId,
destructs: Slice3<IdentId, MonoFieldId, DestructType>,
},
List {
elem_type: MonoTypeId,
patterns: Slice<MonoPatternId>,
/// 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<IdentId>)>,
},
Underscore,
}
#[derive(Clone, Copy, Debug)]
pub enum DestructType {
Required,
Optional(MonoTypeId, MonoExprId),
Guard(MonoTypeId, MonoPatternId),
}

View file

@ -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<String>,
debug_info: DebugInfo,
}
impl MonoModule {
pub fn from_typed_can_module(_subs: &Solved<Subs>) -> 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<InternedStrId> {
self.interned
.iter()
.position(|&interned| interned == string)
.map(|index| InternedStrId(index as u32))
}
}

View file

@ -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),
}

View file

@ -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
}
}

View file

@ -0,0 +1,296 @@
use core::num::NonZeroU16;
use soa::{Index, NonEmptySlice, Slice};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MonoTypeId {
inner: Index<MonoType>,
}
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<MonoType>) -> Self {
Self { inner }
}
}
#[derive(Debug, Default)]
pub struct MonoTypes {
entries: Vec<MonoType>,
ids: Vec<MonoTypeId>,
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<MonoTypeId>,
args: impl IntoIterator<Item = MonoTypeId>,
) -> 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<Item = MonoTypeId>,
) -> 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<Item = MonoTypeId>,
) -> 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<Item = MonoTypeId>) -> Slice<MonoTypeId> {
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<MonoTypeId>),
/// 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<MonoTypeId>),
/// 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<MonoTypeId>,
},
/// 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<MonoTypeId>,
},
// 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),
}

View file

@ -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<MonoType>, // `None` means it specialized to Unit
),
CharSpecializedToWrongType(
Option<MonoType>, // `None` means it specialized to Unit
),
BadNumTypeParam,
UninitializedReservedExpr,
FnDidNotHaveFnType,
}
/// For MonoTypes that are records, store their field indices.
pub type RecordFieldIds = VecMap<MonoTypeId, VecMap<Lowercase, MonoFieldId>>;
/// 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<MonoTypeId, VecMap<u16, MonoFieldId>>;
/// Variables that have already been monomorphized.
pub struct MonoTypeCache {
inner: VecMap<Variable, MonoTypeId>,
}
impl MonoTypeCache {
pub fn from_solved_subs(subs: &Solved<Subs>) -> 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<Problem>,
debug_info: &mut Option<DebugInfo>,
var: Variable,
) -> Option<MonoTypeId> {
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<DebugInfo>,
}
impl<'a, 'c, 'd, 'e, 'f, 'm, 'p, P: Push<Problem>> Env<'a, 'c, 'd, 'e, 'f, 'm, 'p, P> {
fn lower_builtin(
&mut self,
subs: &Subs,
symbol: Symbol,
args: SubsSlice<Variable>,
) -> 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<Variable>,
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<MonoTypeId> {
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<Variable>,
subs: &Subs,
problems: &mut impl Push<Problem>,
) -> 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<Variable>,
subs: &Subs,
problems: &mut impl Push<Problem>,
) -> 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<Problem>,
// mut tags: UnionTags,
// mut ext: TagExt,
// ) -> Vec<(TagName, Vec<Variable>)> {
// 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<P: Push<Problem>>(
// env: &mut Env<'_, '_, '_, '_, '_, '_, P>,
// subs: &Subs,
// mut fields: RecordFields,
// mut ext: Variable,
// ) -> Vec<(Lowercase, Option<MonoTypeId>)> {
// 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<Problem>,
// 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<Item = &'a mut Variable>,
// cache: &mut MonoCache,
// subs: &mut Subs,
// mono_types: &mut MonoTypes,
// problems: &mut impl Push<Problem>,
// ) {
// 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 = ;
// }
// }

View file

@ -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<DebugInfo> = 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<str>) {
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<str>, mono_expr: MonoExpr) {
expect_mono_expr_with_interns(input, |_, _| mono_expr);
}
#[track_caller]
pub fn expect_mono_expr_str(input: impl AsRef<str>, expr_str: impl AsRef<str>) {
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<str>,
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<T: PartialEq + core::fmt::Debug>(
input: impl AsRef<str>,
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);
}

View file

@ -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)),
);
}
}

View file

@ -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}))])"),
);
}
}

View file

@ -439,6 +439,7 @@ fn check_derived_typechecks_and_golden(
derived_module: Default::default(), derived_module: Default::default(),
module_params: None, module_params: None,
module_params_vars: imported_param_vars, module_params_vars: imported_param_vars,
host_exposed_symbols: None,
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
checkmate: None, checkmate: None,

View file

@ -1607,7 +1607,7 @@ fn tail_call_elimination() {
#[test] #[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] #[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 123i8", -123, i8);
assert_evals_to!("Num.neg Num.maxI8", -i8::MAX, i8); assert_evals_to!("Num.neg Num.maxI8", -i8::MAX, i8);
assert_evals_to!("Num.neg (Num.minI8 + 1)", 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 123", -123, i64);
assert_evals_to!("Num.neg Num.maxI64", -i64::MAX, 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 (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] #[test]

View file

@ -542,6 +542,7 @@ pub fn try_run_lib_function<T>(
#[allow(dead_code)] #[allow(dead_code)]
// only used in tests // only used in tests
#[allow(dead_code)]
pub(crate) fn llvm_evals_to<T, U, F>( pub(crate) fn llvm_evals_to<T, U, F>(
src: &str, src: &str,
expected: U, expected: U,

View file

@ -4,7 +4,7 @@ To setup fuzzing you will need to install cargo-fuzz and run with rust nightly:
```sh ```sh
$ cargo install cargo-fuzz $ cargo install cargo-fuzz
$ cargo +nightly fuzz run -j<cores> <target> -- -dict=../parse/fuzz/dict.txt $ cargo +nightly fuzz run -j<cores> <target> -- -dict=../dict.txt
``` ```
The different targets can be found by running `cargo fuzz list`. The different targets can be found by running `cargo fuzz list`.

View file

@ -7,10 +7,10 @@ Defs(
@0-3, @0-3,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [ type_defs: [

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-43, @0-43,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [ type_defs: [

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-52, @0-52,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [ type_defs: [

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-55, @0-55,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [ type_defs: [

View file

@ -10,12 +10,12 @@ SpaceAfter(
@53-104, @53-104,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 0, length: 2 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
Slice { start: 2, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
], ],
spaces: [ spaces: [
Newline, Newline,

View file

@ -7,10 +7,10 @@ Defs(
@0-38, @0-38,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-89, @0-89,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -7,10 +7,10 @@ Defs(
@0-39, @0-39,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-49, @0-49,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-46, @0-46,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -8,10 +8,10 @@ SpaceAfter(
@0-41, @0-41,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

View file

@ -9,10 +9,10 @@ SpaceBefore(
@107-112, @107-112,
], ],
space_before: [ space_before: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
space_after: [ space_after: [
Slice { start: 0, length: 0 }, Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
], ],
spaces: [], spaces: [],
type_defs: [], type_defs: [],

Some files were not shown because too many files have changed in this diff Show more