mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 05:49:08 +00:00
Merge branch 'roc-lang:main' into int_overflow
This commit is contained in:
commit
62db49f7d2
231 changed files with 5522 additions and 834 deletions
14
.github/workflows/nix_linux_x86_64.yml
vendored
14
.github/workflows/nix_linux_x86_64.yml
vendored
|
@ -18,13 +18,23 @@ jobs:
|
|||
run: nix-build
|
||||
|
||||
- name: execute tests with --release
|
||||
run: nix develop -c cargo test --locked --release
|
||||
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
|
||||
run: nix develop -c cargo test --locked --release -- --skip glue_cli_tests
|
||||
|
||||
- name: glue_cli_tests
|
||||
# single threaded due to difficult bug when multithreading
|
||||
run: nix develop -c cargo test --locked --release glue_cli_tests -- --test-threads=1
|
||||
|
||||
- name: roc test all builtins
|
||||
run: nix develop -c ./ci/roc_test_builtins.sh
|
||||
|
||||
- name: test wasm32 cli_tests
|
||||
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run"
|
||||
# skipping glue tests due to difficult multithreading bug, we run them single threaded in the next step
|
||||
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" -- --skip glue_cli_tests
|
||||
|
||||
- name: wasm32 glue_cli_tests
|
||||
# single threaded due to difficult bug when multithreading
|
||||
run: nix develop -c cargo test --locked --release --features="wasm32-cli-run" glue_cli_tests -- --test-threads=1
|
||||
|
||||
- name: test the dev backend # these tests require an explicit feature flag
|
||||
run: nix develop -c cargo nextest run --locked --release --package test_gen --no-default-features --features gen-dev --no-fail-fast
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -59,6 +59,7 @@ metadata
|
|||
#editors
|
||||
.idea/
|
||||
.vscode/
|
||||
.helix/
|
||||
.ignore
|
||||
.exrc
|
||||
.vimrc
|
||||
|
|
57
Cargo.lock
generated
57
Cargo.lock
generated
|
@ -2354,6 +2354,7 @@ dependencies = [
|
|||
"roc_types",
|
||||
"soa",
|
||||
"static_assertions",
|
||||
"test_compile",
|
||||
"ven_pretty",
|
||||
]
|
||||
|
||||
|
@ -2630,7 +2631,6 @@ dependencies = [
|
|||
"cli_test_utils",
|
||||
"dircpy",
|
||||
"fnv",
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"libc",
|
||||
"libloading",
|
||||
|
@ -2954,7 +2954,6 @@ dependencies = [
|
|||
"rustyline-derive",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3140,6 +3139,36 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_specialize_types"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.4",
|
||||
"bitvec",
|
||||
"bumpalo",
|
||||
"hashbrown",
|
||||
"indoc",
|
||||
"parking_lot",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"soa",
|
||||
"static_assertions",
|
||||
"test_compile",
|
||||
"test_solve_helpers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.0.1"
|
||||
|
@ -3781,6 +3810,30 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_compile"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_constrain",
|
||||
"roc_derive",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_parse",
|
||||
"roc_problem",
|
||||
"roc_region",
|
||||
"roc_reporting",
|
||||
"roc_solve",
|
||||
"roc_solve_problem",
|
||||
"roc_specialize_types",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "test_derive"
|
||||
version = "0.0.1"
|
||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -17,6 +17,7 @@ members = [
|
|||
"crates/repl_wasm",
|
||||
"crates/repl_expect",
|
||||
"crates/roc_std",
|
||||
"crates/test_compile",
|
||||
"crates/test_utils",
|
||||
"crates/test_utils_dir",
|
||||
"crates/valgrind_tests",
|
||||
|
@ -75,11 +76,9 @@ version = "0.0.1"
|
|||
inkwell = { git = "https://github.com/roc-lang/inkwell", branch = "inkwell-llvm-16", features = [
|
||||
"llvm16-0",
|
||||
] }
|
||||
|
||||
soa = { path = "crates/soa" }
|
||||
|
||||
roc_specialize_types = { path = "crates/compiler/specialize_types" }
|
||||
arrayvec = "0.7.2" # update roc_std/Cargo.toml on change
|
||||
backtrace = "0.3.67"
|
||||
base64-url = "1.4.13"
|
||||
bincode = "1.3.3"
|
||||
bitflags = "1.3.2"
|
||||
|
@ -87,9 +86,7 @@ bitvec = "1.0.1"
|
|||
blake3 = "1.3.3"
|
||||
brotli = "3.3.4" # used for decompressing tarballs over HTTPS, if the server supports brotli
|
||||
bumpalo = { version = "3.12.0", features = ["collections"] }
|
||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||
capstone = { version = "0.11.0", default-features = false }
|
||||
cgmath = "0.18.0"
|
||||
chrono = "0.4.26"
|
||||
clap = { version = "4.2.7", default-features = false, features = [
|
||||
"std",
|
||||
|
@ -99,10 +96,8 @@ clap = { version = "4.2.7", default-features = false, features = [
|
|||
"usage",
|
||||
"error-context",
|
||||
] }
|
||||
colored = "2.0.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
const_format = { version = "0.2.30", features = ["const_generics"] }
|
||||
copypasta = "0.8.2"
|
||||
criterion = { git = "https://github.com/Anton-4/criterion.rs", features = [
|
||||
"html_reports",
|
||||
], rev = "30ea0c5" }
|
||||
|
@ -116,7 +111,6 @@ flate2 = "1.0.25"
|
|||
fnv = "1.0.7"
|
||||
fs_extra = "1.3.0"
|
||||
futures = "0.3.26"
|
||||
glyph_brush = "0.7.7"
|
||||
hashbrown = { version = "0.14.3" }
|
||||
iced-x86 = { version = "1.18.0", default-features = false, features = [
|
||||
"std",
|
||||
|
@ -126,7 +120,6 @@ iced-x86 = { version = "1.18.0", default-features = false, features = [
|
|||
] }
|
||||
im = "15.1.0"
|
||||
im-rc = "15.1.0"
|
||||
indexmap = "2.1.0"
|
||||
indoc = "1.0.9"
|
||||
insta = "1.28.0"
|
||||
js-sys = "0.3.61"
|
||||
|
@ -140,18 +133,13 @@ mach_object = "0.1"
|
|||
maplit = "1.0.2"
|
||||
memmap2 = "0.5.10"
|
||||
mimalloc = { version = "0.1.34", default-features = false }
|
||||
nonempty = "0.8.1"
|
||||
object = { version = "0.32.2", default-features = false, features = [
|
||||
"read",
|
||||
"write",
|
||||
] }
|
||||
packed_struct = "0.10.1"
|
||||
page_size = "0.5.0"
|
||||
palette = "0.6.1"
|
||||
parking_lot = "0.12"
|
||||
perfcnt = "0.8.0"
|
||||
pest = "2.5.6"
|
||||
pest_derive = "2.5.6"
|
||||
pretty_assertions = "1.3.0" # update roc_std/Cargo.toml on change
|
||||
proc-macro2 = "1.0.63"
|
||||
proptest = "1.1.0"
|
||||
|
@ -185,21 +173,17 @@ syn = { version = "1.0.109", features = ["full", "extra-traits"] }
|
|||
tar = "0.4.38"
|
||||
target-lexicon = "0.12.6"
|
||||
tempfile = "=3.2.0"
|
||||
threadpool = "1.8.1"
|
||||
tracing = { version = "0.1.40", features = ["release_max_level_off"] }
|
||||
tracing-appender = "0.2.2"
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
unicode-segmentation = "1.10.1"
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
walkdir = "2.3.2"
|
||||
wasm-bindgen = "0.2.84"
|
||||
wasm-bindgen-futures = "0.4.34"
|
||||
wgpu = "0.12.0"
|
||||
wgpu_glyph = "0.16.0"
|
||||
winapi = { version = "0.3.9", features = ["memoryapi"] }
|
||||
winit = "0.26.1"
|
||||
wyhash = "0.5.0"
|
||||
|
||||
# Testing
|
||||
test_compile = { path = "crates/test_compile" }
|
||||
|
||||
# Optimizations based on https://deterministic.space/high-performance-rust.html
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
|
|
@ -30,3 +30,4 @@ soa.workspace = true
|
|||
indoc.workspace = true
|
||||
insta.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
test_compile.workspace = true
|
||||
|
|
|
@ -132,7 +132,7 @@ pub struct AbleVariable {
|
|||
pub first_seen: Region,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct IntroducedVariables {
|
||||
pub wildcards: Vec<Loc<Variable>>,
|
||||
pub lambda_sets: Vec<Variable>,
|
||||
|
|
|
@ -929,7 +929,7 @@ pub struct FxExpectation {
|
|||
pub ann_region: Option<Region>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum FxCallKind {
|
||||
Call(Option<Symbol>),
|
||||
Stmt,
|
||||
|
@ -943,7 +943,7 @@ pub struct FxSuffixConstraint {
|
|||
pub region: Region,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum FxSuffixKind {
|
||||
Let(Symbol),
|
||||
Pattern(Symbol),
|
||||
|
@ -957,9 +957,16 @@ impl FxSuffixKind {
|
|||
Self::UnsuffixedRecordField => IdentSuffix::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn symbol(&self) -> Option<&Symbol> {
|
||||
match self {
|
||||
Self::Let(symbol) | Self::Pattern(symbol) => Some(symbol),
|
||||
Self::UnsuffixedRecordField => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ExpectEffectfulReason {
|
||||
Stmt,
|
||||
Ignored,
|
||||
|
|
|
@ -63,7 +63,7 @@ use std::io::Read;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Def {
|
||||
pub loc_pattern: Loc<Pattern>,
|
||||
pub loc_expr: Loc<Expr>,
|
||||
|
@ -91,7 +91,7 @@ impl Def {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum DefKind {
|
||||
/// A def that introduces identifiers
|
||||
Let,
|
||||
|
@ -123,7 +123,7 @@ impl DefKind {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Annotation {
|
||||
pub signature: Type,
|
||||
pub introduced_variables: IntroducedVariables,
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::pattern::Pattern;
|
|||
use roc_region::all::{Loc, Region};
|
||||
use roc_types::types::{AnnotationSource, PReason, Reason};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Expected<T> {
|
||||
NoExpectation(T),
|
||||
FromAnnotation(Loc<Pattern>, usize, AnnotationSource, T),
|
||||
|
@ -10,7 +10,7 @@ pub enum Expected<T> {
|
|||
}
|
||||
|
||||
/// Like Expected, but for Patterns.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PExpected<T> {
|
||||
NoExpectation(T),
|
||||
ForReason(PReason, T, Region),
|
||||
|
|
|
@ -85,7 +85,55 @@ impl Display for IntValue {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
impl IntValue {
|
||||
pub fn as_u8(self) -> u8 {
|
||||
self.as_u128() as u8
|
||||
}
|
||||
|
||||
pub fn as_i8(self) -> i8 {
|
||||
self.as_i128() as i8
|
||||
}
|
||||
|
||||
pub fn as_u16(self) -> u16 {
|
||||
self.as_u128() as u16
|
||||
}
|
||||
|
||||
pub fn as_i16(self) -> i16 {
|
||||
self.as_i128() as i16
|
||||
}
|
||||
|
||||
pub fn as_u32(self) -> u32 {
|
||||
self.as_u128() as u32
|
||||
}
|
||||
|
||||
pub fn as_i32(self) -> i32 {
|
||||
self.as_i128() as i32
|
||||
}
|
||||
|
||||
pub fn as_u64(self) -> u64 {
|
||||
self.as_u128() as u64
|
||||
}
|
||||
|
||||
pub fn as_i64(self) -> i64 {
|
||||
self.as_i128() as i64
|
||||
}
|
||||
|
||||
pub fn as_u128(self) -> u128 {
|
||||
match self {
|
||||
IntValue::I128(i128) => i128::from_ne_bytes(i128) as u128,
|
||||
IntValue::U128(u128) => u128::from_ne_bytes(u128),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_i128(self) -> i128 {
|
||||
match self {
|
||||
IntValue::I128(i128) => i128::from_ne_bytes(i128),
|
||||
IntValue::U128(u128) => u128::from_ne_bytes(u128) as i128,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Expr {
|
||||
// Literals
|
||||
|
||||
|
@ -104,7 +152,7 @@ pub enum Expr {
|
|||
loc_elems: Vec<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),
|
||||
|
||||
// Lookups
|
||||
|
@ -131,7 +179,7 @@ pub enum Expr {
|
|||
/// The actual condition of the when expression.
|
||||
loc_cond: Box<Loc<Expr>>,
|
||||
cond_var: Variable,
|
||||
/// Result type produced by the branches.
|
||||
/// Type of each branch (and therefore the type of the entire `when` expression)
|
||||
expr_var: Variable,
|
||||
region: Region,
|
||||
/// The branches of the when, and the type of the condition that they expect to be matched
|
||||
|
@ -292,7 +340,7 @@ pub enum Expr {
|
|||
RuntimeError(RuntimeError),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ExpectLookup {
|
||||
pub symbol: Symbol,
|
||||
pub var: Variable,
|
||||
|
@ -370,7 +418,7 @@ impl Expr {
|
|||
|
||||
/// Stores exhaustiveness-checking metadata for a closure argument that may
|
||||
/// have an annotated type.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct AnnotatedMark {
|
||||
pub annotation_var: Variable,
|
||||
pub exhaustive: ExhaustiveMark,
|
||||
|
@ -394,7 +442,7 @@ impl AnnotatedMark {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ClosureData {
|
||||
pub function_type: Variable,
|
||||
pub closure_type: Variable,
|
||||
|
@ -491,7 +539,7 @@ impl StructAccessorData {
|
|||
/// An opaque wrapper like `@Foo`, which is equivalent to `\p -> @Foo p`
|
||||
/// These are desugared to closures, but we distinguish them so we can have
|
||||
/// better error messages during constraint generation.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct OpaqueWrapFunctionData {
|
||||
pub opaque_name: Symbol,
|
||||
pub opaque_var: Variable,
|
||||
|
@ -563,7 +611,7 @@ impl OpaqueWrapFunctionData {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Field {
|
||||
pub var: Variable,
|
||||
// The region of the full `foo: f bar`, rather than just `f bar`
|
||||
|
@ -587,7 +635,7 @@ impl Recursive {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenBranchPattern {
|
||||
pub pattern: Loc<Pattern>,
|
||||
/// Degenerate branch patterns are those that don't fully bind symbols that the branch body
|
||||
|
@ -596,7 +644,7 @@ pub struct WhenBranchPattern {
|
|||
pub degenerate: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WhenBranch {
|
||||
pub patterns: Vec<WhenBranchPattern>,
|
||||
pub value: Loc<Expr>,
|
||||
|
|
|
@ -19,7 +19,7 @@ use roc_types::types::{LambdaSet, OptAbleVar, PatternCategory, Type};
|
|||
|
||||
/// A pattern, including possible problems (e.g. shadowing) so that
|
||||
/// codegen can generate a runtime error if this pattern is reached.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
Identifier(Symbol),
|
||||
As(Box<Loc<Pattern>>, Symbol),
|
||||
|
@ -198,7 +198,7 @@ impl Pattern {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ListPatterns {
|
||||
pub patterns: Vec<Loc<Pattern>>,
|
||||
/// Where a rest pattern splits patterns before and after it, if it does at all.
|
||||
|
@ -207,6 +207,7 @@ pub struct ListPatterns {
|
|||
/// [ .., A, B ] -> patterns = [A, B], rest = 0
|
||||
/// [ A, .., B ] -> patterns = [A, B], rest = 1
|
||||
/// [ A, B, .. ] -> patterns = [A, B], rest = 2
|
||||
/// Optionally, the rest pattern can be named - e.g. `[ A, B, ..others ]`
|
||||
pub opt_rest: Option<(usize, Option<Loc<Symbol>>)>,
|
||||
}
|
||||
|
||||
|
@ -228,7 +229,7 @@ impl ListPatterns {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RecordDestruct {
|
||||
pub var: Variable,
|
||||
pub label: Lowercase,
|
||||
|
@ -236,14 +237,14 @@ pub struct RecordDestruct {
|
|||
pub typ: DestructType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TupleDestruct {
|
||||
pub var: Variable,
|
||||
pub destruct_index: usize,
|
||||
pub typ: (Variable, Loc<Pattern>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum DestructType {
|
||||
Required,
|
||||
Optional(Variable, Loc<Expr>),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -65,10 +66,10 @@ Defs {
|
|||
@15-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-25,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -49,10 +50,10 @@ Defs {
|
|||
@15-19,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-43,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -59,10 +60,10 @@ Defs {
|
|||
@15-33,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -59,10 +60,10 @@ Defs {
|
|||
@15-35,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -49,10 +50,10 @@ Defs {
|
|||
@15-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -39,10 +40,10 @@ Defs {
|
|||
@11-14,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -49,10 +50,10 @@ Defs {
|
|||
@16-35,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -11,10 +11,10 @@ Defs {
|
|||
@0-69,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -34,10 +34,10 @@ Defs {
|
|||
@15-57,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -67,10 +67,10 @@ Defs {
|
|||
@31-43,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-114,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@39-101,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -84,10 +85,10 @@ Defs {
|
|||
@82-91,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-143,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@56-119,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -112,10 +113,10 @@ Defs {
|
|||
@76-83,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -181,10 +182,10 @@ Defs {
|
|||
@92-99,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@0-26,
|
||||
@0-28,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -24,88 +25,90 @@ Defs {
|
|||
@0-4 Identifier {
|
||||
ident: "main",
|
||||
},
|
||||
@11-26 Defs(
|
||||
@11-28 Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@15-26,
|
||||
@16-27,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@15-26 Identifier {
|
||||
@16-27 Identifier {
|
||||
ident: "1",
|
||||
},
|
||||
@15-26 ParensAround(
|
||||
@16-27 ParensAround(
|
||||
Defs(
|
||||
Defs {
|
||||
tags: [
|
||||
EitherIndex(2147483648),
|
||||
],
|
||||
regions: [
|
||||
@20-25,
|
||||
@21-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
value_defs: [
|
||||
Body(
|
||||
@20-25 Identifier {
|
||||
@21-26 Identifier {
|
||||
ident: "0",
|
||||
},
|
||||
@20-25 Apply(
|
||||
@22-23 Var {
|
||||
module_name: "Num",
|
||||
ident: "add",
|
||||
},
|
||||
[
|
||||
@20-21 Num(
|
||||
"1",
|
||||
@21-26 ParensAround(
|
||||
Apply(
|
||||
@23-24 Var {
|
||||
module_name: "Num",
|
||||
ident: "add",
|
||||
},
|
||||
[
|
||||
@21-22 Num(
|
||||
"1",
|
||||
),
|
||||
@25-26 Num(
|
||||
"1",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Plus,
|
||||
),
|
||||
@24-25 Num(
|
||||
"1",
|
||||
),
|
||||
],
|
||||
BinOp(
|
||||
Plus,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
@15-26 LowLevelDbg(
|
||||
@16-27 LowLevelDbg(
|
||||
(
|
||||
"test.roc:3",
|
||||
" ",
|
||||
),
|
||||
@20-25 Apply(
|
||||
@20-25 Var {
|
||||
@21-26 Apply(
|
||||
@21-26 Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toStr",
|
||||
},
|
||||
[
|
||||
@20-25 Var {
|
||||
@21-26 Var {
|
||||
module_name: "",
|
||||
ident: "0",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@20-25 Var {
|
||||
@21-26 Var {
|
||||
module_name: "",
|
||||
ident: "0",
|
||||
},
|
||||
|
@ -115,25 +118,25 @@ Defs {
|
|||
),
|
||||
],
|
||||
},
|
||||
@11-26 LowLevelDbg(
|
||||
@11-28 LowLevelDbg(
|
||||
(
|
||||
"test.roc:2",
|
||||
"in =\n ",
|
||||
"n =\n ",
|
||||
),
|
||||
@15-26 Apply(
|
||||
@15-26 Var {
|
||||
@16-27 Apply(
|
||||
@16-27 Var {
|
||||
module_name: "Inspect",
|
||||
ident: "toStr",
|
||||
},
|
||||
[
|
||||
@15-26 Var {
|
||||
@16-27 Var {
|
||||
module_name: "",
|
||||
ident: "1",
|
||||
},
|
||||
],
|
||||
Space,
|
||||
),
|
||||
@15-26 Var {
|
||||
@16-27 Var {
|
||||
module_name: "",
|
||||
ident: "1",
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-99,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -12,12 +13,12 @@ Defs {
|
|||
@56-98,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -39,10 +40,10 @@ Defs {
|
|||
@15-20,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -74,10 +75,10 @@ Defs {
|
|||
@25-39,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -151,10 +152,10 @@ Defs {
|
|||
@75-80,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-158,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -45,10 +46,10 @@ Defs {
|
|||
@50-52,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-31,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@11-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-307,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -37,14 +38,14 @@ Defs {
|
|||
@109-298,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice { start: 1, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 1 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 1, length: 0 },
|
||||
Slice { start: 2, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -171,10 +172,10 @@ Defs {
|
|||
@140-152,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -293,10 +294,10 @@ Defs {
|
|||
@227-239,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-189,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -35,12 +36,12 @@ Defs {
|
|||
@52-70,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 1, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -14,14 +15,14 @@ Defs {
|
|||
@229-266,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice { start: 2, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 0 },
|
||||
Slice { start: 4, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 4, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -120,12 +121,12 @@ Defs {
|
|||
@203-208,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 1, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -12,12 +13,12 @@ Defs {
|
|||
@35-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@15-17,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-33,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -39,10 +40,10 @@ Defs {
|
|||
@11-14,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -99,10 +100,10 @@ Defs {
|
|||
@20-23,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-26,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -11,10 +11,10 @@ Defs {
|
|||
@0-72,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -40,10 +40,10 @@ Defs {
|
|||
@11-23,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-51,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@17-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-24,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -39,10 +40,10 @@ Defs {
|
|||
@11-16,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-61,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@23-42,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-22,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-51,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -33,10 +34,10 @@ Defs {
|
|||
@11-40,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
@ -60,10 +61,10 @@ Defs {
|
|||
@11-12,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -11,10 +11,10 @@ Defs {
|
|||
@0-73,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -40,10 +40,10 @@ Defs {
|
|||
@11-57,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-67,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -48,10 +49,10 @@ Defs {
|
|||
@19-30,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-154,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-44,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -45,10 +46,10 @@ Defs {
|
|||
@28-29,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-45,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-120,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
@ -67,10 +68,10 @@ Defs {
|
|||
@54-65,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
source: crates/compiler/can/tests/test_suffixed.rs
|
||||
expression: snapshot
|
||||
snapshot_kind: text
|
||||
---
|
||||
Defs {
|
||||
tags: [
|
||||
|
@ -10,10 +11,10 @@ Defs {
|
|||
@0-74,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 1 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
13
crates/compiler/can/tests/test_can_expr.rs
Normal file
13
crates/compiler/can/tests/test_can_expr.rs
Normal 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));
|
||||
}
|
||||
}
|
|
@ -464,7 +464,7 @@ mod suffixed_tests {
|
|||
run_test!(
|
||||
r#"
|
||||
main =
|
||||
dbg (dbg 1 + 1)
|
||||
dbg (dbg (1 + 1))
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
pub mod all;
|
||||
mod push;
|
||||
mod reference_matrix;
|
||||
mod small_string_interner;
|
||||
mod small_vec;
|
||||
|
@ -12,6 +13,7 @@ mod vec_map;
|
|||
mod vec_set;
|
||||
|
||||
pub use all::{default_hasher, BumpMap, ImEntry, ImMap, ImSet, MutMap, MutSet, SendMap};
|
||||
pub use push::Push;
|
||||
pub use reference_matrix::{ReferenceMatrix, Sccs, TopologicalSort};
|
||||
pub use small_string_interner::SmallStringInterner;
|
||||
pub use small_vec::SmallVec;
|
||||
|
|
15
crates/compiler/collections/src/push.rs
Normal file
15
crates/compiler/collections/src/push.rs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -64,6 +64,15 @@ pub struct Env {
|
|||
}
|
||||
|
||||
impl Env {
|
||||
pub fn new(home: ModuleId) -> Self {
|
||||
Self {
|
||||
rigids: MutMap::default(),
|
||||
resolutions_to_make: Vec::new(),
|
||||
home,
|
||||
fx_expectation: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_fx_expectation<F, T>(
|
||||
&mut self,
|
||||
fx_var: Variable,
|
||||
|
|
|
@ -447,7 +447,7 @@ impl<'a> Formattable for Expr<'a> {
|
|||
buf.push_str("dbg");
|
||||
}
|
||||
DbgStmt(condition, continuation) => {
|
||||
fmt_dbg_stmt(buf, condition, continuation, self.is_multiline(), indent);
|
||||
fmt_dbg_stmt(buf, condition, continuation, parens, indent);
|
||||
}
|
||||
LowLevelDbg(_, _, _) => unreachable!(
|
||||
"LowLevelDbg should only exist after desugaring, not during formatting"
|
||||
|
@ -1022,42 +1022,15 @@ fn fmt_dbg_stmt<'a>(
|
|||
buf: &mut Buf,
|
||||
condition: &'a Loc<Expr<'a>>,
|
||||
continuation: &'a Loc<Expr<'a>>,
|
||||
_: bool,
|
||||
parens: Parens,
|
||||
indent: u16,
|
||||
) {
|
||||
buf.ensure_ends_with_newline();
|
||||
buf.indent(indent);
|
||||
buf.push_str("dbg");
|
||||
|
||||
buf.spaces(1);
|
||||
|
||||
fn should_outdent(mut expr: &Expr) -> bool {
|
||||
loop {
|
||||
match expr {
|
||||
Expr::ParensAround(_) | Expr::List(_) | Expr::Record(_) | Expr::Tuple(_) => {
|
||||
return true
|
||||
}
|
||||
Expr::SpaceAfter(inner, _) => {
|
||||
expr = inner;
|
||||
}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let inner_indent = if should_outdent(&condition.value) {
|
||||
indent
|
||||
} else {
|
||||
indent + INDENT
|
||||
};
|
||||
|
||||
let cond_value = condition.value.extract_spaces();
|
||||
|
||||
let is_defs = matches!(cond_value.item, Expr::Defs(_, _));
|
||||
|
||||
let newlines = if is_defs { Newlines::Yes } else { Newlines::No };
|
||||
|
||||
condition.format_with_options(buf, Parens::NotNeeded, newlines, inner_indent);
|
||||
Expr::Apply(
|
||||
&Loc::at_zero(Expr::Dbg),
|
||||
&[condition],
|
||||
called_via::CalledVia::Space,
|
||||
)
|
||||
.format_with_options(buf, parens, Newlines::Yes, indent);
|
||||
|
||||
// Always put a blank line after the `dbg` line(s)
|
||||
buf.ensure_ends_with_blank_line();
|
||||
|
|
|
@ -1846,6 +1846,26 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
|||
neg_reg64_reg64(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
_relocs: &mut Vec<'_, Relocation>,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
fneg_freg_freg(buf, FloatWidth::F64, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
_relocs: &mut Vec<'_, Relocation>,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
fneg_freg_freg(buf, FloatWidth::F32, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
@ -3953,6 +3973,24 @@ fn fsub_freg_freg_freg(
|
|||
buf.extend(inst.bytes());
|
||||
}
|
||||
|
||||
/// `FNEG Sd/Dd, Sn/Dn`
|
||||
#[inline(always)]
|
||||
fn fneg_freg_freg(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
ftype: FloatWidth,
|
||||
dst: AArch64FloatReg,
|
||||
src: AArch64FloatReg,
|
||||
) {
|
||||
let inst =
|
||||
FloatingPointDataProcessingOneSource::new(FloatingPointDataProcessingOneSourceParams {
|
||||
ptype: ftype,
|
||||
opcode: 0b00010,
|
||||
rn: src,
|
||||
rd: dst,
|
||||
});
|
||||
buf.extend(inst.bytes());
|
||||
}
|
||||
|
||||
/// `FCMP Sn/Dn, Sm/Dm` -> Compare Sn/Dn and Sm/Dm, setting condition flags.
|
||||
#[inline(always)]
|
||||
fn fcmp_freg_freg(
|
||||
|
|
|
@ -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 neg_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: FloatReg,
|
||||
src: FloatReg,
|
||||
);
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: FloatReg,
|
||||
src: FloatReg,
|
||||
);
|
||||
fn mul_freg32_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
dst: FloatReg,
|
||||
|
@ -1791,7 +1803,24 @@ impl<
|
|||
let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src);
|
||||
ASM::neg_reg64_reg64(&mut self.buf, dst_reg, src_reg);
|
||||
}
|
||||
x => todo!("NumNeg: layout, {:?}", x),
|
||||
LayoutRepr::F32 => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src);
|
||||
ASM::neg_freg32_freg32(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
|
||||
}
|
||||
LayoutRepr::F64 => {
|
||||
let dst_reg = self.storage_manager.claim_float_reg(&mut self.buf, dst);
|
||||
let src_reg = self.storage_manager.load_to_float_reg(&mut self.buf, src);
|
||||
ASM::neg_freg64_freg64(&mut self.buf, &mut self.relocs, dst_reg, src_reg);
|
||||
}
|
||||
LayoutRepr::DEC => self.build_fn_call(
|
||||
dst,
|
||||
bitcode::DEC_NEGATE.to_string(),
|
||||
&[*src],
|
||||
&[Layout::DEC],
|
||||
&Layout::DEC,
|
||||
),
|
||||
other => internal_error!("unreachable: NumNeg for layout, {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2602,6 +2602,28 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
|||
neg_reg64(buf, dst);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg64_freg64(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: X86_64FloatReg,
|
||||
src: X86_64FloatReg,
|
||||
) {
|
||||
Self::mov_freg64_imm64(buf, relocs, dst, f64::from_bits(0x8000_0000_0000_0000));
|
||||
xorpd_freg64_freg64(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn neg_freg32_freg32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
relocs: &mut Vec<'_, Relocation>,
|
||||
dst: X86_64FloatReg,
|
||||
src: X86_64FloatReg,
|
||||
) {
|
||||
Self::mov_freg32_imm32(buf, relocs, dst, f32::from_bits(0x8000_0000));
|
||||
xorps_freg32_freg32(buf, dst, src);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sub_reg64_reg64_imm32(
|
||||
buf: &mut Vec<'_, u8>,
|
||||
|
@ -3352,6 +3374,49 @@ fn sqrtss_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64F
|
|||
}
|
||||
}
|
||||
|
||||
/// `XORPD xmm1, xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1.
|
||||
#[inline(always)]
|
||||
fn xorpd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = src as u8 % 8;
|
||||
|
||||
if dst_high || src_high {
|
||||
buf.extend([
|
||||
0x66,
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x57,
|
||||
0xC0 | (dst_mod << 3) | src_mod,
|
||||
])
|
||||
} else {
|
||||
buf.extend([0x66, 0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
/// `XORPS xmm1,xmm2/m128` -> Bitwise exclusive-OR of xmm2/m128 and xmm1.
|
||||
#[inline(always)]
|
||||
fn xorps_freg32_freg32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||
let dst_high = dst as u8 > 7;
|
||||
let dst_mod = dst as u8 % 8;
|
||||
|
||||
let src_high = src as u8 > 7;
|
||||
let src_mod = src as u8 % 8;
|
||||
|
||||
if dst_high || src_high {
|
||||
buf.extend([
|
||||
0x40 | ((dst_high as u8) << 2) | (src_high as u8),
|
||||
0x0F,
|
||||
0x57,
|
||||
0xC0 | (dst_mod << 3) | src_mod,
|
||||
]);
|
||||
} else {
|
||||
buf.extend([0x0F, 0x57, 0xC0 | (dst_mod << 3) | src_mod]);
|
||||
}
|
||||
}
|
||||
|
||||
/// `TEST r/m64,r64` -> AND r64 with r/m64; set SF, ZF, PF according to result.
|
||||
#[allow(dead_code)]
|
||||
#[inline(always)]
|
||||
|
|
|
@ -2241,6 +2241,7 @@ fn build_dec_unary_op<'a, 'ctx>(
|
|||
|
||||
match op {
|
||||
NumAbs => dec_unary_op(env, bitcode::DEC_ABS, arg),
|
||||
NumNeg => dec_unary_op(env, bitcode::DEC_NEGATE, arg),
|
||||
NumAcos => dec_unary_op(env, bitcode::DEC_ACOS, arg),
|
||||
NumAsin => dec_unary_op(env, bitcode::DEC_ASIN, arg),
|
||||
NumAtan => dec_unary_op(env, bitcode::DEC_ATAN, arg),
|
||||
|
|
|
@ -1624,6 +1624,7 @@ impl<'a> LowLevelCall<'a> {
|
|||
}
|
||||
F32 => backend.code_builder.f32_neg(),
|
||||
F64 => backend.code_builder.f64_neg(),
|
||||
Decimal => self.load_args_and_call_zig(backend, bitcode::DEC_NEGATE),
|
||||
_ => todo!("{:?} for {:?}", self.lowlevel, self.ret_layout),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ pub fn infer_expr(
|
|||
function_kind: FunctionKind::LambdaSet,
|
||||
module_params: None,
|
||||
module_params_vars: Default::default(),
|
||||
host_exposed_symbols: None,
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate: None,
|
||||
};
|
||||
|
@ -189,6 +190,24 @@ pub fn can_expr_with<'a>(
|
|||
// rules multiple times unnecessarily.
|
||||
let loc_expr = desugar::desugar_expr(&mut env, &mut scope, &loc_expr);
|
||||
|
||||
let mut scope = Scope::new(
|
||||
home,
|
||||
"TestPath".into(),
|
||||
IdentIds::default(),
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let dep_idents = IdentIds::exposed_builtins(0);
|
||||
let mut env = Env::new(
|
||||
arena,
|
||||
expr_str,
|
||||
home,
|
||||
Path::new("Test.roc"),
|
||||
&dep_idents,
|
||||
&module_ids,
|
||||
None,
|
||||
roc_can::env::FxMode::PurityInference,
|
||||
);
|
||||
let (loc_expr, output) = canonicalize_expr(
|
||||
&mut env,
|
||||
&mut var_store,
|
||||
|
|
|
@ -11054,8 +11054,8 @@ All branches in an `if` must have the same type!
|
|||
4│ Recursive := [Infinitely Recursive]
|
||||
^^^^^^^^^
|
||||
|
||||
Recursion in opaquees is only allowed if recursion happens behind a
|
||||
tagged union, at least one variant of which is not recursive.
|
||||
Recursion in opaque types is only allowed if recursion happens behind
|
||||
a tagged union, at least one variant of which is not recursive.
|
||||
"
|
||||
);
|
||||
|
||||
|
@ -14812,7 +14812,7 @@ All branches in an `if` must have the same type!
|
|||
Str.trim msg
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
@r#"
|
||||
── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ──────────────────────────────
|
||||
|
||||
This call to `Effect.putLine!` might produce an effect:
|
||||
|
@ -14829,18 +14829,7 @@ All branches in an `if` must have the same type!
|
|||
|
||||
You can still run the program with this error, which can be helpful
|
||||
when you're debugging.
|
||||
|
||||
── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
|
||||
|
||||
This function is pure, but its name suggests otherwise:
|
||||
|
||||
5│ main! = \{} ->
|
||||
^^^^^
|
||||
|
||||
The exclamation mark at the end is reserved for effectful functions.
|
||||
|
||||
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
||||
"###
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -15423,7 +15412,7 @@ All branches in an `if` must have the same type!
|
|||
pureHigherOrder = \f, x -> f x
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
@r#"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This 1st argument to `pureHigherOrder` has an unexpected type:
|
||||
|
@ -15438,18 +15427,7 @@ All branches in an `if` must have the same type!
|
|||
But `pureHigherOrder` needs its 1st argument to be:
|
||||
|
||||
Str -> {}
|
||||
|
||||
── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
|
||||
|
||||
This function is pure, but its name suggests otherwise:
|
||||
|
||||
5│ main! = \{} ->
|
||||
^^^^^
|
||||
|
||||
The exclamation mark at the end is reserved for effectful functions.
|
||||
|
||||
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
||||
"###
|
||||
"#
|
||||
);
|
||||
|
||||
test_report!(
|
||||
|
@ -15467,7 +15445,7 @@ All branches in an `if` must have the same type!
|
|||
pureHigherOrder = \f, x -> f x
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
@r#"
|
||||
── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
|
||||
|
||||
This 1st argument to `pureHigherOrder` has an unexpected type:
|
||||
|
@ -15482,17 +15460,6 @@ All branches in an `if` must have the same type!
|
|||
But `pureHigherOrder` needs its 1st argument to be:
|
||||
|
||||
Str -> {}
|
||||
|
||||
── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
|
||||
|
||||
This function is pure, but its name suggests otherwise:
|
||||
|
||||
5│ main! = \{} ->
|
||||
^^^^^
|
||||
|
||||
The exclamation mark at the end is reserved for effectful functions.
|
||||
|
||||
Hint: Did you forget to run an effect? Is the type annotation wrong?
|
||||
"###
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
|
|
@ -350,6 +350,8 @@ fn start_phase<'a>(
|
|||
None
|
||||
};
|
||||
|
||||
let is_host_exposed = state.root_id == module.module_id;
|
||||
|
||||
BuildTask::solve_module(
|
||||
module,
|
||||
ident_ids,
|
||||
|
@ -367,6 +369,7 @@ fn start_phase<'a>(
|
|||
state.cached_types.clone(),
|
||||
derived_module,
|
||||
state.exec_mode,
|
||||
is_host_exposed,
|
||||
//
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate,
|
||||
|
@ -922,6 +925,7 @@ enum BuildTask<'a> {
|
|||
cached_subs: CachedTypeState,
|
||||
derived_module: SharedDerivedModule,
|
||||
exec_mode: ExecutionMode,
|
||||
is_host_exposed: bool,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate: Option<roc_checkmate::Collector>,
|
||||
|
@ -4331,6 +4335,7 @@ impl<'a> BuildTask<'a> {
|
|||
cached_subs: CachedTypeState,
|
||||
derived_module: SharedDerivedModule,
|
||||
exec_mode: ExecutionMode,
|
||||
is_host_exposed: bool,
|
||||
|
||||
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
|
||||
) -> Self {
|
||||
|
@ -4355,6 +4360,7 @@ impl<'a> BuildTask<'a> {
|
|||
cached_subs,
|
||||
derived_module,
|
||||
exec_mode,
|
||||
is_host_exposed,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate,
|
||||
|
@ -4661,6 +4667,7 @@ fn run_solve_solve(
|
|||
var_store: VarStore,
|
||||
module: Module,
|
||||
derived_module: SharedDerivedModule,
|
||||
is_host_exposed: bool,
|
||||
|
||||
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
|
||||
) -> SolveResult {
|
||||
|
@ -4711,6 +4718,12 @@ fn run_solve_solve(
|
|||
let (solve_output, solved_implementations, exposed_vars_by_symbol) = {
|
||||
let module_id = module.module_id;
|
||||
|
||||
let host_exposed_idents = if is_host_exposed {
|
||||
Some(&exposed_symbols)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let solve_config = SolveConfig {
|
||||
home: module_id,
|
||||
types,
|
||||
|
@ -4724,6 +4737,7 @@ fn run_solve_solve(
|
|||
checkmate,
|
||||
module_params,
|
||||
module_params_vars: imported_param_vars,
|
||||
host_exposed_symbols: host_exposed_idents,
|
||||
};
|
||||
|
||||
let solve_output = roc_solve::module::run_solve(
|
||||
|
@ -4800,6 +4814,7 @@ fn run_solve<'a>(
|
|||
cached_types: CachedTypeState,
|
||||
derived_module: SharedDerivedModule,
|
||||
exec_mode: ExecutionMode,
|
||||
is_host_exposed: bool,
|
||||
|
||||
#[cfg(debug_assertions)] checkmate: Option<roc_checkmate::Collector>,
|
||||
) -> Msg<'a> {
|
||||
|
@ -4831,6 +4846,7 @@ fn run_solve<'a>(
|
|||
var_store,
|
||||
module,
|
||||
derived_module,
|
||||
is_host_exposed,
|
||||
//
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate,
|
||||
|
@ -4863,6 +4879,7 @@ fn run_solve<'a>(
|
|||
var_store,
|
||||
module,
|
||||
derived_module,
|
||||
is_host_exposed,
|
||||
//
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate,
|
||||
|
@ -6256,6 +6273,7 @@ fn run_task<'a>(
|
|||
cached_subs,
|
||||
derived_module,
|
||||
exec_mode,
|
||||
is_host_exposed,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate,
|
||||
|
@ -6275,6 +6293,7 @@ fn run_task<'a>(
|
|||
cached_subs,
|
||||
derived_module,
|
||||
exec_mode,
|
||||
is_host_exposed,
|
||||
//
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate,
|
||||
|
|
|
@ -35,7 +35,7 @@ use roc_region::all::{Loc, Position, Region};
|
|||
|
||||
use crate::parser::Progress::{self, *};
|
||||
|
||||
fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||
pub fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||
|_arena, state: State<'a>, _min_indent: u32| {
|
||||
if state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
|
@ -545,10 +545,6 @@ fn stmt_start<'a>(
|
|||
EExpr::Expect,
|
||||
expect_help(options, preceding_comment)
|
||||
)),
|
||||
loc(specialize_err(
|
||||
EExpr::Dbg,
|
||||
dbg_stmt_help(options, preceding_comment)
|
||||
)),
|
||||
loc(specialize_err(EExpr::Return, return_help(options))),
|
||||
loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))),
|
||||
map(
|
||||
|
@ -2668,34 +2664,6 @@ fn return_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Stmt<'a>, ERetu
|
|||
.trace("return_help")
|
||||
}
|
||||
|
||||
fn dbg_stmt_help<'a>(
|
||||
options: ExprParseOptions,
|
||||
preceding_comment: Region,
|
||||
) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> {
|
||||
(move |arena: &'a Bump, state: State<'a>, min_indent| {
|
||||
let (_, _, state) =
|
||||
parser::keyword(keyword::DBG, EExpect::Dbg).parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, condition, state) = parse_block(
|
||||
options,
|
||||
arena,
|
||||
state,
|
||||
true,
|
||||
EExpect::IndentCondition,
|
||||
EExpect::Condition,
|
||||
)
|
||||
.map_err(|(_, f)| (MadeProgress, f))?;
|
||||
|
||||
let stmt = Stmt::ValueDef(ValueDef::Dbg {
|
||||
condition: arena.alloc(condition),
|
||||
preceding_comment,
|
||||
});
|
||||
|
||||
Ok((MadeProgress, stmt, state))
|
||||
})
|
||||
.trace("dbg_stmt_help")
|
||||
}
|
||||
|
||||
fn dbg_kw<'a>() -> impl Parser<'a, Expr<'a>, EExpect<'a>> {
|
||||
(move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||
let (_, _, next_state) =
|
||||
|
@ -3110,12 +3078,43 @@ fn stmts_to_defs<'a>(
|
|||
}
|
||||
Stmt::Expr(e) => {
|
||||
if i + 1 < stmts.len() {
|
||||
defs.push_value_def(
|
||||
ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))),
|
||||
sp_stmt.item.region,
|
||||
sp_stmt.before,
|
||||
&[],
|
||||
);
|
||||
if let Expr::Apply(
|
||||
Loc {
|
||||
value: Expr::Dbg, ..
|
||||
},
|
||||
args,
|
||||
_,
|
||||
) = e
|
||||
{
|
||||
if args.len() != 1 {
|
||||
// TODO: this should be done in can, not parsing!
|
||||
return Err(EExpr::Dbg(
|
||||
EExpect::DbgArity(sp_stmt.item.region.start()),
|
||||
sp_stmt.item.region.start(),
|
||||
));
|
||||
}
|
||||
let condition = &args[0];
|
||||
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
|
||||
let e = Expr::DbgStmt(condition, arena.alloc(rest));
|
||||
|
||||
let e = if sp_stmt.before.is_empty() {
|
||||
e
|
||||
} else {
|
||||
arena.alloc(e).before(sp_stmt.before)
|
||||
};
|
||||
|
||||
last_expr = Some(Loc::at(sp_stmt.item.region, e));
|
||||
|
||||
// don't re-process the rest of the statements; they got consumed by the dbg expr
|
||||
break;
|
||||
} else {
|
||||
defs.push_value_def(
|
||||
ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))),
|
||||
sp_stmt.item.region,
|
||||
sp_stmt.before,
|
||||
&[],
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let e = if sp_stmt.before.is_empty() {
|
||||
e
|
||||
|
|
|
@ -1465,6 +1465,7 @@ impl<'a> Normalize<'a> for EExpect<'a> {
|
|||
EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero())
|
||||
}
|
||||
EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()),
|
||||
EExpect::DbgArity(_) => EExpect::DbgArity(Position::zero()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -513,6 +513,7 @@ pub enum EExpect<'a> {
|
|||
Condition(&'a EExpr<'a>, Position),
|
||||
Continuation(&'a EExpr<'a>, Position),
|
||||
IndentCondition(Position),
|
||||
DbgArity(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -6,7 +6,7 @@ use roc_can::constraint::{Constraint, Constraints};
|
|||
use roc_can::expr::PendingDerives;
|
||||
use roc_can::module::{ExposedByModule, ModuleParams, ResolvedImplementations, RigidVariables};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_collections::VecMap;
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
|
@ -76,6 +76,8 @@ pub struct SolveConfig<'a> {
|
|||
/// Needed during solving to resolve lambda sets from derived implementations that escape into
|
||||
/// the user module.
|
||||
pub derived_module: SharedDerivedModule,
|
||||
/// Symbols that are exposed to the host which might need special treatment.
|
||||
pub host_exposed_symbols: Option<&'a VecSet<Symbol>>,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
/// The checkmate collector for this module.
|
||||
|
|
|
@ -19,7 +19,7 @@ use roc_can::constraint::{
|
|||
};
|
||||
use roc_can::expected::{Expected, PExpected};
|
||||
use roc_can::module::ModuleParams;
|
||||
use roc_collections::VecMap;
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_debug_flags::dbg_do;
|
||||
#[cfg(debug_assertions)]
|
||||
use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED;
|
||||
|
@ -136,6 +136,7 @@ fn run_help(
|
|||
function_kind,
|
||||
module_params,
|
||||
module_params_vars,
|
||||
host_exposed_symbols,
|
||||
..
|
||||
} = config;
|
||||
|
||||
|
@ -190,6 +191,7 @@ fn run_help(
|
|||
&mut awaiting_specializations,
|
||||
module_params,
|
||||
module_params_vars,
|
||||
host_exposed_symbols,
|
||||
);
|
||||
|
||||
RunSolveOutput {
|
||||
|
@ -249,6 +251,7 @@ fn solve(
|
|||
awaiting_specializations: &mut AwaitingSpecializations,
|
||||
module_params: Option<ModuleParams>,
|
||||
module_params_vars: VecMap<ModuleId, Variable>,
|
||||
host_exposed_symbols: Option<&VecSet<Symbol>>,
|
||||
) -> State {
|
||||
let scope = Scope::new(module_params);
|
||||
|
||||
|
@ -455,6 +458,7 @@ fn solve(
|
|||
solve_suffix_fx(
|
||||
env,
|
||||
problems,
|
||||
host_exposed_symbols,
|
||||
FxSuffixKind::Let(*symbol),
|
||||
loc_var.value,
|
||||
&loc_var.region,
|
||||
|
@ -853,7 +857,7 @@ fn solve(
|
|||
*type_index,
|
||||
);
|
||||
|
||||
solve_suffix_fx(env, problems, *kind, actual, region);
|
||||
solve_suffix_fx(env, problems, host_exposed_symbols, *kind, actual, region);
|
||||
state
|
||||
}
|
||||
ExpectEffectful(variable, reason, region) => {
|
||||
|
@ -1625,6 +1629,7 @@ fn solve(
|
|||
fn solve_suffix_fx(
|
||||
env: &mut InferenceEnv<'_>,
|
||||
problems: &mut Vec<TypeError>,
|
||||
host_exposed_symbols: Option<&VecSet<Symbol>>,
|
||||
kind: FxSuffixKind,
|
||||
variable: Variable,
|
||||
region: &Region,
|
||||
|
@ -1651,7 +1656,16 @@ fn solve_suffix_fx(
|
|||
let fx = *fx;
|
||||
match env.subs.get_content_without_compacting(fx) {
|
||||
Content::Pure => {
|
||||
problems.push(TypeError::SuffixedPureFunction(*region, kind));
|
||||
match (kind.symbol(), host_exposed_symbols) {
|
||||
(Some(sym), Some(host_exposed)) if host_exposed.contains(sym) => {
|
||||
// If exposed to the platform, it's allowed to be suffixed but pure
|
||||
// The platform might require a `main!` function that could perform
|
||||
// effects, but that's not a requirement.
|
||||
}
|
||||
_ => {
|
||||
problems.push(TypeError::SuffixedPureFunction(*region, kind));
|
||||
}
|
||||
}
|
||||
}
|
||||
Content::FlexVar(_) => {
|
||||
env.subs.set_content(fx, Content::Effectful);
|
||||
|
|
|
@ -15,7 +15,7 @@ use roc_region::all::Region;
|
|||
|
||||
use roc_types::types::{Category, ErrorType, PatternCategory};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum TypeError {
|
||||
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
||||
BadPattern(Region, PatternCategory, ErrorType, PExpected<ErrorType>),
|
||||
|
|
40
crates/compiler/specialize_types/Cargo.toml
Normal file
40
crates/compiler/specialize_types/Cargo.toml
Normal 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
|
8
crates/compiler/specialize_types/src/debug_info.rs
Normal file
8
crates/compiler/specialize_types/src/debug_info.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#[derive(Default)]
|
||||
pub struct DebugInfo;
|
||||
|
||||
impl DebugInfo {
|
||||
pub fn new() -> Self {
|
||||
DebugInfo
|
||||
}
|
||||
}
|
651
crates/compiler/specialize_types/src/expr.rs
Normal file
651
crates/compiler/specialize_types/src/expr.rs
Normal 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,
|
||||
}
|
||||
}
|
37
crates/compiler/specialize_types/src/foreign_symbol.rs
Normal file
37
crates/compiler/specialize_types/src/foreign_symbol.rs
Normal 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>,
|
||||
}
|
24
crates/compiler/specialize_types/src/lib.rs
Normal file
24
crates/compiler/specialize_types/src/lib.rs
Normal 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};
|
502
crates/compiler/specialize_types/src/mono_expr.rs
Normal file
502
crates/compiler/specialize_types/src/mono_expr.rs
Normal 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
|
||||
// }
|
||||
// }
|
||||
// }
|
372
crates/compiler/specialize_types/src/mono_ir.rs
Normal file
372
crates/compiler/specialize_types/src/mono_ir.rs
Normal 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),
|
||||
}
|
78
crates/compiler/specialize_types/src/mono_module.rs
Normal file
78
crates/compiler/specialize_types/src/mono_module.rs
Normal 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))
|
||||
}
|
||||
}
|
16
crates/compiler/specialize_types/src/mono_num.rs
Normal file
16
crates/compiler/specialize_types/src/mono_num.rs
Normal 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),
|
||||
}
|
13
crates/compiler/specialize_types/src/mono_struct.rs
Normal file
13
crates/compiler/specialize_types/src/mono_struct.rs
Normal 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
|
||||
}
|
||||
}
|
296
crates/compiler/specialize_types/src/mono_type.rs
Normal file
296
crates/compiler/specialize_types/src/mono_type.rs
Normal 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),
|
||||
}
|
650
crates/compiler/specialize_types/src/specialize_type.rs
Normal file
650
crates/compiler/specialize_types/src/specialize_type.rs
Normal 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 = ;
|
||||
// }
|
||||
// }
|
282
crates/compiler/specialize_types/tests/helpers/mod.rs
Normal file
282
crates/compiler/specialize_types/tests/helpers/mod.rs
Normal 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);
|
||||
}
|
173
crates/compiler/specialize_types/tests/specialize_primitives.rs
Normal file
173
crates/compiler/specialize_types/tests/specialize_primitives.rs
Normal 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)),
|
||||
);
|
||||
}
|
||||
}
|
54
crates/compiler/specialize_types/tests/specialize_structs.rs
Normal file
54
crates/compiler/specialize_types/tests/specialize_structs.rs
Normal 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}))])"),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -439,6 +439,7 @@ fn check_derived_typechecks_and_golden(
|
|||
derived_module: Default::default(),
|
||||
module_params: None,
|
||||
module_params_vars: imported_param_vars,
|
||||
host_exposed_symbols: None,
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
checkmate: None,
|
||||
|
|
|
@ -1607,7 +1607,7 @@ fn tail_call_elimination() {
|
|||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn int_negate() {
|
||||
fn num_negate() {
|
||||
assert_evals_to!("Num.neg 123i8", -123, i8);
|
||||
assert_evals_to!("Num.neg Num.maxI8", -i8::MAX, i8);
|
||||
assert_evals_to!("Num.neg (Num.minI8 + 1)", i8::MAX, i8);
|
||||
|
@ -1623,6 +1623,26 @@ fn int_negate() {
|
|||
assert_evals_to!("Num.neg 123", -123, i64);
|
||||
assert_evals_to!("Num.neg Num.maxI64", -i64::MAX, i64);
|
||||
assert_evals_to!("Num.neg (Num.minI64 + 1)", i64::MAX, i64);
|
||||
|
||||
assert_evals_to!("Num.neg 12.3f32", -12.3, f32);
|
||||
assert_evals_to!("Num.neg 0.0f32", -0.0, f32);
|
||||
assert_evals_to!("Num.neg Num.maxF32", -f32::MAX, f32);
|
||||
assert_evals_to!("Num.neg Num.minF32", -f32::MIN, f32);
|
||||
assert_evals_to!("Num.neg Num.infinityF32", -f32::INFINITY, f32);
|
||||
// can't test equality for nan
|
||||
assert_evals_to!("Num.isNaN (Num.neg Num.nanF32)", true, bool);
|
||||
|
||||
assert_evals_to!("Num.neg 12.3f64", -12.3, f64);
|
||||
assert_evals_to!("Num.neg 0.0f64", -0.0, f64);
|
||||
assert_evals_to!("Num.neg Num.maxF64", -f64::MAX, f64);
|
||||
assert_evals_to!("Num.neg Num.minF64", -f64::MIN, f64);
|
||||
assert_evals_to!("Num.neg Num.infinityF64", -f64::INFINITY, f64);
|
||||
// can't test equality for nan
|
||||
assert_evals_to!("Num.isNaN (Num.neg Num.nanF64)", true, bool);
|
||||
|
||||
assert_evals_to!("Num.neg 123dec", RocDec::from(-123), RocDec);
|
||||
// 0 is signless, unlike f32/f64
|
||||
assert_evals_to!("Num.neg 0dec", RocDec::from(0), RocDec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -542,6 +542,7 @@ pub fn try_run_lib_function<T>(
|
|||
|
||||
#[allow(dead_code)]
|
||||
// only used in tests
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn llvm_evals_to<T, U, F>(
|
||||
src: &str,
|
||||
expected: U,
|
||||
|
|
|
@ -4,7 +4,7 @@ To setup fuzzing you will need to install cargo-fuzz and run with rust nightly:
|
|||
|
||||
```sh
|
||||
$ cargo install cargo-fuzz
|
||||
$ cargo +nightly fuzz run -j<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`.
|
||||
|
|
|
@ -7,10 +7,10 @@ Defs(
|
|||
@0-3,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-43,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-52,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-55,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [
|
||||
|
|
|
@ -10,12 +10,12 @@ SpaceAfter(
|
|||
@53-104,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 0, length: 2 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 2 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice { start: 2, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 2, length: 0 },
|
||||
],
|
||||
spaces: [
|
||||
Newline,
|
||||
|
|
|
@ -7,10 +7,10 @@ Defs(
|
|||
@0-38,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-89,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -7,10 +7,10 @@ Defs(
|
|||
@0-39,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-49,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-46,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -8,10 +8,10 @@ SpaceAfter(
|
|||
@0-41,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
|
@ -9,10 +9,10 @@ SpaceBefore(
|
|||
@107-112,
|
||||
],
|
||||
space_before: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
space_after: [
|
||||
Slice { start: 0, length: 0 },
|
||||
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||
],
|
||||
spaces: [],
|
||||
type_defs: [],
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue