mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 14:24:45 +00:00
Merge remote-tracking branch 'origin/trunk' into hostgen
This commit is contained in:
commit
36f64d8496
45 changed files with 1801 additions and 1368 deletions
72
Cargo.lock
generated
72
Cargo.lock
generated
|
@ -468,9 +468,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "3.1.15"
|
version = "3.1.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85a35a599b11c089a7f49105658d089b8f2cf0882993c17daf6de15285c2c35d"
|
checksum = "47582c09be7c8b32c0ab3a6181825ababb713fde6fff20fc573a3870dd45c6a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
@ -516,6 +516,7 @@ name = "cli_utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
|
"const_format",
|
||||||
"criterion 0.3.5 (git+https://github.com/Anton-4/criterion.rs)",
|
"criterion 0.3.5 (git+https://github.com/Anton-4/criterion.rs)",
|
||||||
"rlimit",
|
"rlimit",
|
||||||
"roc_cli",
|
"roc_cli",
|
||||||
|
@ -1674,9 +1675,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glyph_brush"
|
name = "glyph_brush"
|
||||||
version = "0.7.3"
|
version = "0.7.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21932fbf719272848eec4583740d978203c6e7da4c4e203358f5b95946c97409"
|
checksum = "a69c65dd1f1fbb6209aa00f78636e436ad0a55b7d8e5de886d00720dcad9c6e2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glyph_brush_draw_cache",
|
"glyph_brush_draw_cache",
|
||||||
"glyph_brush_layout",
|
"glyph_brush_layout",
|
||||||
|
@ -1865,12 +1866,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indoc"
|
name = "indoc"
|
||||||
version = "1.0.4"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
|
checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e"
|
||||||
dependencies = [
|
|
||||||
"unindent",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inkwell"
|
name = "inkwell"
|
||||||
|
@ -2688,9 +2686,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "2.10.0"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
|
checksum = "96bcbab4bfea7a59c2c0fe47211a1ac4e3e96bea6eb446d704f310bc5c732ae2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
@ -3085,9 +3083,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.37"
|
version = "1.0.38"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
@ -3435,7 +3433,7 @@ name = "roc-bindgen"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"clap 3.1.15",
|
"clap 3.1.17",
|
||||||
"indoc",
|
"indoc",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
|
@ -3546,7 +3544,6 @@ dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc",
|
"indoc",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"roc_builtins",
|
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_exhaustive",
|
"roc_exhaustive",
|
||||||
|
@ -3556,7 +3553,6 @@ dependencies = [
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"static_assertions 1.1.0",
|
"static_assertions 1.1.0",
|
||||||
"ven_graph",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3564,7 +3560,7 @@ name = "roc_cli"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"clap 3.1.15",
|
"clap 3.1.17",
|
||||||
"cli_utils",
|
"cli_utils",
|
||||||
"const_format",
|
"const_format",
|
||||||
"criterion 0.3.5 (git+https://github.com/Anton-4/criterion.rs)",
|
"criterion 0.3.5 (git+https://github.com/Anton-4/criterion.rs)",
|
||||||
|
@ -3590,6 +3586,8 @@ dependencies = [
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_test_utils",
|
"roc_test_utils",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
"strum",
|
||||||
|
"strum_macros",
|
||||||
"target-lexicon",
|
"target-lexicon",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"wasmer",
|
"wasmer",
|
||||||
|
@ -3645,7 +3643,6 @@ name = "roc_docs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc",
|
|
||||||
"peg",
|
"peg",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
|
@ -3663,8 +3660,6 @@ dependencies = [
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"snafu",
|
"snafu",
|
||||||
"tempfile",
|
|
||||||
"uuid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3813,9 +3808,6 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_ident"
|
name = "roc_ident"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
|
||||||
"arrayvec 0.7.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_linker"
|
name = "roc_linker"
|
||||||
|
@ -3823,7 +3815,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"clap 3.1.15",
|
"clap 3.1.17",
|
||||||
"iced-x86",
|
"iced-x86",
|
||||||
"memmap2 0.5.3",
|
"memmap2 0.5.3",
|
||||||
"object 0.26.2",
|
"object 0.26.2",
|
||||||
|
@ -3858,8 +3850,6 @@ dependencies = [
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
"indoc",
|
"indoc",
|
||||||
"maplit",
|
"maplit",
|
||||||
"morphic_lib",
|
|
||||||
"num_cpus",
|
|
||||||
"parking_lot 0.12.0",
|
"parking_lot 0.12.0",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
|
@ -3879,7 +3869,6 @@ dependencies = [
|
||||||
"roc_test_utils",
|
"roc_test_utils",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"tempfile",
|
|
||||||
"ven_pretty",
|
"ven_pretty",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3887,7 +3876,6 @@ dependencies = [
|
||||||
name = "roc_module"
|
name = "roc_module"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.7.2",
|
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -3904,7 +3892,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"hashbrown 0.11.2",
|
"hashbrown 0.11.2",
|
||||||
"morphic_lib",
|
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -4047,7 +4034,6 @@ dependencies = [
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_test_utils",
|
"roc_test_utils",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"tempfile",
|
|
||||||
"ven_pretty",
|
"ven_pretty",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4690,6 +4676,25 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e96acfc1b70604b8b2f1ffa4c57e59176c7dbb05d556c71ecd2f5498a1dee7f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6878079b17446e4d3eba6192bb0a2950d5b14f0ed8424b852310e5a94345d0ef"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.92"
|
version = "1.0.92"
|
||||||
|
@ -4778,7 +4783,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc",
|
"indoc",
|
||||||
"pretty_assertions",
|
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -5016,12 +5020,6 @@ version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unindent"
|
|
||||||
version = "0.1.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match load_types(input_path.clone(), &cwd, Threading::Multi) {
|
match load_types(input_path.clone(), &cwd, Threading::AllAvailable) {
|
||||||
Ok(types) => {
|
Ok(types) => {
|
||||||
let mut buf;
|
let mut buf;
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,8 @@ indoc = "1.0.3"
|
||||||
serial_test = "0.5.1"
|
serial_test = "0.5.1"
|
||||||
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
criterion = { git = "https://github.com/Anton-4/criterion.rs"}
|
||||||
cli_utils = { path = "../cli_utils" }
|
cli_utils = { path = "../cli_utils" }
|
||||||
|
strum = "0.24.0"
|
||||||
|
strum_macros = "0.24"
|
||||||
|
|
||||||
# Wasmer singlepass compiler only works on x86_64.
|
# Wasmer singlepass compiler only works on x86_64.
|
||||||
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dev-dependencies]
|
||||||
|
|
|
@ -350,6 +350,7 @@ pub fn check_file(
|
||||||
src_dir: PathBuf,
|
src_dir: PathBuf,
|
||||||
roc_file_path: PathBuf,
|
roc_file_path: PathBuf,
|
||||||
emit_timings: bool,
|
emit_timings: bool,
|
||||||
|
threading: Threading,
|
||||||
) -> Result<(program::Problems, Duration), LoadingProblem> {
|
) -> Result<(program::Problems, Duration), LoadingProblem> {
|
||||||
let compilation_start = SystemTime::now();
|
let compilation_start = SystemTime::now();
|
||||||
|
|
||||||
|
@ -368,7 +369,7 @@ pub fn check_file(
|
||||||
target_info,
|
target_info,
|
||||||
// TODO: expose this from CLI?
|
// TODO: expose this from CLI?
|
||||||
RenderTarget::ColorTerminal,
|
RenderTarget::ColorTerminal,
|
||||||
Threading::Multi,
|
threading,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let buf = &mut String::with_capacity(1024);
|
let buf = &mut String::with_capacity(1024);
|
||||||
|
|
|
@ -2,29 +2,17 @@ use std::ffi::OsStr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use crate::FormatMode;
|
use crate::FormatMode;
|
||||||
use bumpalo::collections::Vec;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_error_macros::{internal_error, user_error};
|
use roc_error_macros::{internal_error, user_error};
|
||||||
use roc_fmt::def::fmt_def;
|
use roc_fmt::def::fmt_def;
|
||||||
use roc_fmt::module::fmt_module;
|
use roc_fmt::module::fmt_module;
|
||||||
use roc_fmt::Buf;
|
use roc_fmt::spaces::RemoveSpaces;
|
||||||
use roc_module::called_via::{BinOp, UnaryOp};
|
use roc_fmt::{Ast, Buf};
|
||||||
use roc_parse::ast::{
|
|
||||||
AbilityMember, AssignedField, Collection, Expr, Has, HasClause, Pattern, Spaced, StrLiteral,
|
|
||||||
StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, WhenBranch,
|
|
||||||
};
|
|
||||||
use roc_parse::header::{
|
|
||||||
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
|
|
||||||
PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
|
|
||||||
};
|
|
||||||
use roc_parse::{
|
use roc_parse::{
|
||||||
ast::{Def, Module},
|
|
||||||
ident::UppercaseIdent,
|
|
||||||
module::{self, module_defs},
|
module::{self, module_defs},
|
||||||
parser::{Parser, SyntaxError},
|
parser::{Parser, SyntaxError},
|
||||||
state::State,
|
state::State,
|
||||||
};
|
};
|
||||||
use roc_region::all::{Loc, Region};
|
|
||||||
|
|
||||||
fn flatten_directories(files: std::vec::Vec<PathBuf>) -> std::vec::Vec<PathBuf> {
|
fn flatten_directories(files: std::vec::Vec<PathBuf>) -> std::vec::Vec<PathBuf> {
|
||||||
let mut to_flatten = files;
|
let mut to_flatten = files;
|
||||||
|
@ -166,12 +154,6 @@ pub fn format(files: std::vec::Vec<PathBuf>, mode: FormatMode) -> Result<(), Str
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
struct Ast<'a> {
|
|
||||||
module: Module<'a>,
|
|
||||||
defs: Vec<'a, Loc<Def<'a>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result<Ast<'a>, SyntaxError<'a>> {
|
fn parse_all<'a>(arena: &'a Bump, src: &'a str) -> Result<Ast<'a>, SyntaxError<'a>> {
|
||||||
let (module, state) = module::parse_header(arena, State::new(src.as_bytes()))
|
let (module, state) = module::parse_header(arena, State::new(src.as_bytes()))
|
||||||
.map_err(|e| SyntaxError::Header(e.problem))?;
|
.map_err(|e| SyntaxError::Header(e.problem))?;
|
||||||
|
@ -189,575 +171,3 @@ fn fmt_all<'a>(arena: &'a Bump, buf: &mut Buf<'a>, ast: &'a Ast) {
|
||||||
|
|
||||||
buf.fmt_end_of_file();
|
buf.fmt_end_of_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
|
|
||||||
///
|
|
||||||
/// Currently this consists of:
|
|
||||||
/// * Removing newlines
|
|
||||||
/// * Removing comments
|
|
||||||
/// * Removing parens in Exprs
|
|
||||||
///
|
|
||||||
/// Long term, we actuall want this transform to preserve comments (so we can assert they're maintained by formatting)
|
|
||||||
/// - but there are currently several bugs where they're _not_ preserved.
|
|
||||||
/// TODO: ensure formatting retains comments
|
|
||||||
trait RemoveSpaces<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Ast<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
Ast {
|
|
||||||
module: self.module.remove_spaces(arena),
|
|
||||||
defs: {
|
|
||||||
let mut defs = Vec::with_capacity_in(self.defs.len(), arena);
|
|
||||||
for d in &self.defs {
|
|
||||||
defs.push(d.remove_spaces(arena))
|
|
||||||
}
|
|
||||||
defs
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Module<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match self {
|
|
||||||
Module::Interface { header } => Module::Interface {
|
|
||||||
header: InterfaceHeader {
|
|
||||||
name: header.name.remove_spaces(arena),
|
|
||||||
exposes: header.exposes.remove_spaces(arena),
|
|
||||||
imports: header.imports.remove_spaces(arena),
|
|
||||||
before_header: &[],
|
|
||||||
after_interface_keyword: &[],
|
|
||||||
before_exposes: &[],
|
|
||||||
after_exposes: &[],
|
|
||||||
before_imports: &[],
|
|
||||||
after_imports: &[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Module::App { header } => Module::App {
|
|
||||||
header: AppHeader {
|
|
||||||
name: header.name.remove_spaces(arena),
|
|
||||||
packages: header.packages.remove_spaces(arena),
|
|
||||||
imports: header.imports.remove_spaces(arena),
|
|
||||||
provides: header.provides.remove_spaces(arena),
|
|
||||||
provides_types: header.provides_types.map(|ts| ts.remove_spaces(arena)),
|
|
||||||
to: header.to.remove_spaces(arena),
|
|
||||||
before_header: &[],
|
|
||||||
after_app_keyword: &[],
|
|
||||||
before_packages: &[],
|
|
||||||
after_packages: &[],
|
|
||||||
before_imports: &[],
|
|
||||||
after_imports: &[],
|
|
||||||
before_provides: &[],
|
|
||||||
after_provides: &[],
|
|
||||||
before_to: &[],
|
|
||||||
after_to: &[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Module::Platform { header } => Module::Platform {
|
|
||||||
header: PlatformHeader {
|
|
||||||
name: header.name.remove_spaces(arena),
|
|
||||||
requires: header.requires.remove_spaces(arena),
|
|
||||||
exposes: header.exposes.remove_spaces(arena),
|
|
||||||
packages: header.packages.remove_spaces(arena),
|
|
||||||
imports: header.imports.remove_spaces(arena),
|
|
||||||
provides: header.provides.remove_spaces(arena),
|
|
||||||
before_header: &[],
|
|
||||||
after_platform_keyword: &[],
|
|
||||||
before_requires: &[],
|
|
||||||
after_requires: &[],
|
|
||||||
before_exposes: &[],
|
|
||||||
after_exposes: &[],
|
|
||||||
before_packages: &[],
|
|
||||||
after_packages: &[],
|
|
||||||
before_imports: &[],
|
|
||||||
after_imports: &[],
|
|
||||||
before_provides: &[],
|
|
||||||
after_provides: &[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Module::Hosted { header } => Module::Hosted {
|
|
||||||
header: HostedHeader {
|
|
||||||
name: header.name.remove_spaces(arena),
|
|
||||||
exposes: header.exposes.remove_spaces(arena),
|
|
||||||
imports: header.imports.remove_spaces(arena),
|
|
||||||
generates: header.generates.remove_spaces(arena),
|
|
||||||
generates_with: header.generates_with.remove_spaces(arena),
|
|
||||||
before_header: &[],
|
|
||||||
after_hosted_keyword: &[],
|
|
||||||
before_exposes: &[],
|
|
||||||
after_exposes: &[],
|
|
||||||
before_imports: &[],
|
|
||||||
after_imports: &[],
|
|
||||||
before_generates: &[],
|
|
||||||
after_generates: &[],
|
|
||||||
before_with: &[],
|
|
||||||
after_with: &[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for &'a str {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
|
|
||||||
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for To<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
To::ExistingPackage(a) => To::ExistingPackage(a),
|
|
||||||
To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
TypedIdent {
|
|
||||||
ident: self.ident.remove_spaces(arena),
|
|
||||||
spaces_before_colon: &[],
|
|
||||||
ann: self.ann.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
PlatformRequires {
|
|
||||||
rigids: self.rigids.remove_spaces(arena),
|
|
||||||
signature: self.signature.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
PackageEntry {
|
|
||||||
shorthand: self.shorthand,
|
|
||||||
spaces_after_shorthand: &[],
|
|
||||||
package_name: self.package_name.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
|
|
||||||
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option<T> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
self.as_ref().map(|a| a.remove_spaces(arena))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc<T> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
let res = self.value.remove_spaces(arena);
|
|
||||||
Loc::at(Region::zero(), res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
(self.0.remove_spaces(arena), self.1.remove_spaces(arena))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
let mut items = Vec::with_capacity_in(self.items.len(), arena);
|
|
||||||
for item in self.items {
|
|
||||||
items.push(item.remove_spaces(arena));
|
|
||||||
}
|
|
||||||
Collection::with_items(items.into_bump_slice())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
let mut items = Vec::with_capacity_in(self.len(), arena);
|
|
||||||
for item in *self {
|
|
||||||
let res = item.remove_spaces(arena);
|
|
||||||
items.push(res);
|
|
||||||
}
|
|
||||||
items.into_bump_slice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for UnaryOp {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for BinOp {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
arena.alloc((*self).remove_spaces(arena))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
use TypeDef::*;
|
|
||||||
|
|
||||||
match *self {
|
|
||||||
Alias {
|
|
||||||
header: TypeHeader { name, vars },
|
|
||||||
ann,
|
|
||||||
} => Alias {
|
|
||||||
header: TypeHeader {
|
|
||||||
name: name.remove_spaces(arena),
|
|
||||||
vars: vars.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
ann: ann.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
Opaque {
|
|
||||||
header: TypeHeader { name, vars },
|
|
||||||
typ,
|
|
||||||
} => Opaque {
|
|
||||||
header: TypeHeader {
|
|
||||||
name: name.remove_spaces(arena),
|
|
||||||
vars: vars.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
typ: typ.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
Ability {
|
|
||||||
header: TypeHeader { name, vars },
|
|
||||||
loc_has,
|
|
||||||
members,
|
|
||||||
} => Ability {
|
|
||||||
header: TypeHeader {
|
|
||||||
name: name.remove_spaces(arena),
|
|
||||||
vars: vars.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
loc_has: loc_has.remove_spaces(arena),
|
|
||||||
members: members.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
use ValueDef::*;
|
|
||||||
|
|
||||||
match *self {
|
|
||||||
Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)),
|
|
||||||
Body(a, b) => Body(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
AnnotatedBody {
|
|
||||||
ann_pattern,
|
|
||||||
ann_type,
|
|
||||||
comment: _,
|
|
||||||
body_pattern,
|
|
||||||
body_expr,
|
|
||||||
} => AnnotatedBody {
|
|
||||||
ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)),
|
|
||||||
ann_type: arena.alloc(ann_type.remove_spaces(arena)),
|
|
||||||
comment: None,
|
|
||||||
body_pattern: arena.alloc(body_pattern.remove_spaces(arena)),
|
|
||||||
body_expr: arena.alloc(body_expr.remove_spaces(arena)),
|
|
||||||
},
|
|
||||||
Expect(a) => Expect(arena.alloc(a.remove_spaces(arena))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Def<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
Def::Type(def) => Def::Type(def.remove_spaces(arena)),
|
|
||||||
Def::Value(def) => Def::Value(def.remove_spaces(arena)),
|
|
||||||
Def::NotYetImplemented(a) => Def::NotYetImplemented(a),
|
|
||||||
Def::SpaceBefore(a, _) | Def::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Has<'a> {
|
|
||||||
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
|
||||||
Has::Has
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for AbilityMember<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
AbilityMember {
|
|
||||||
name: self.name.remove_spaces(arena),
|
|
||||||
typ: self.typ.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for WhenBranch<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
WhenBranch {
|
|
||||||
patterns: self.patterns.remove_spaces(arena),
|
|
||||||
value: self.value.remove_spaces(arena),
|
|
||||||
guard: self.guard.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue(
|
|
||||||
a.remove_spaces(arena),
|
|
||||||
arena.alloc([]),
|
|
||||||
arena.alloc(c.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue(
|
|
||||||
a.remove_spaces(arena),
|
|
||||||
arena.alloc([]),
|
|
||||||
arena.alloc(c.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)),
|
|
||||||
AssignedField::Malformed(a) => AssignedField::Malformed(a),
|
|
||||||
AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for StrLiteral<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
|
|
||||||
StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)),
|
|
||||||
StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for StrSegment<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
StrSegment::Plaintext(t) => StrSegment::Plaintext(t),
|
|
||||||
StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)),
|
|
||||||
StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c),
|
|
||||||
StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
Expr::Float(a) => Expr::Float(a),
|
|
||||||
Expr::Num(a) => Expr::Num(a),
|
|
||||||
Expr::NonBase10Int {
|
|
||||||
string,
|
|
||||||
base,
|
|
||||||
is_negative,
|
|
||||||
} => Expr::NonBase10Int {
|
|
||||||
string,
|
|
||||||
base,
|
|
||||||
is_negative,
|
|
||||||
},
|
|
||||||
Expr::Str(a) => Expr::Str(a.remove_spaces(arena)),
|
|
||||||
Expr::Access(a, b) => Expr::Access(arena.alloc(a.remove_spaces(arena)), b),
|
|
||||||
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
|
|
||||||
Expr::List(a) => Expr::List(a.remove_spaces(arena)),
|
|
||||||
Expr::RecordUpdate { update, fields } => Expr::RecordUpdate {
|
|
||||||
update: arena.alloc(update.remove_spaces(arena)),
|
|
||||||
fields: fields.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
|
||||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
|
||||||
Expr::Underscore(a) => Expr::Underscore(a),
|
|
||||||
Expr::Tag(a) => Expr::Tag(a),
|
|
||||||
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
|
||||||
Expr::Closure(a, b) => Expr::Closure(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
Expr::Defs(a, b) => {
|
|
||||||
Expr::Defs(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
|
|
||||||
}
|
|
||||||
Expr::Backpassing(a, b, c) => Expr::Backpassing(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
|
||||||
arena.alloc(c.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
Expr::Expect(a, b) => Expr::Expect(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
Expr::Apply(a, b, c) => Expr::Apply(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
b.remove_spaces(arena),
|
|
||||||
c,
|
|
||||||
),
|
|
||||||
Expr::BinOps(a, b) => {
|
|
||||||
Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
|
|
||||||
}
|
|
||||||
Expr::UnaryOp(a, b) => {
|
|
||||||
Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
|
||||||
}
|
|
||||||
Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))),
|
|
||||||
Expr::When(a, b) => {
|
|
||||||
Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
|
||||||
}
|
|
||||||
Expr::ParensAround(a) => {
|
|
||||||
// The formatter can remove redundant parentheses, so also remove these when normalizing for comparison.
|
|
||||||
a.remove_spaces(arena)
|
|
||||||
}
|
|
||||||
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, b),
|
|
||||||
Expr::MalformedClosure => Expr::MalformedClosure,
|
|
||||||
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
|
||||||
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
Expr::SingleQuote(a) => Expr::Num(a),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
Pattern::Identifier(a) => Pattern::Identifier(a),
|
|
||||||
Pattern::Tag(a) => Pattern::Tag(a),
|
|
||||||
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
|
||||||
Pattern::Apply(a, b) => Pattern::Apply(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)),
|
|
||||||
Pattern::RequiredField(a, b) => {
|
|
||||||
Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena)))
|
|
||||||
}
|
|
||||||
Pattern::OptionalField(a, b) => {
|
|
||||||
Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena)))
|
|
||||||
}
|
|
||||||
Pattern::NumLiteral(a) => Pattern::NumLiteral(a),
|
|
||||||
Pattern::NonBase10Literal {
|
|
||||||
string,
|
|
||||||
base,
|
|
||||||
is_negative,
|
|
||||||
} => Pattern::NonBase10Literal {
|
|
||||||
string,
|
|
||||||
base,
|
|
||||||
is_negative,
|
|
||||||
},
|
|
||||||
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
|
|
||||||
Pattern::StrLiteral(a) => Pattern::StrLiteral(a),
|
|
||||||
Pattern::Underscore(a) => Pattern::Underscore(a),
|
|
||||||
Pattern::Malformed(a) => Pattern::Malformed(a),
|
|
||||||
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, b),
|
|
||||||
Pattern::QualifiedIdentifier { module_name, ident } => {
|
|
||||||
Pattern::QualifiedIdentifier { module_name, ident }
|
|
||||||
}
|
|
||||||
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
Pattern::SingleQuote(a) => Pattern::NumLiteral(a),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
TypeAnnotation::Function(a, b) => TypeAnnotation::Function(
|
|
||||||
arena.alloc(a.remove_spaces(arena)),
|
|
||||||
arena.alloc(b.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)),
|
|
||||||
TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a),
|
|
||||||
TypeAnnotation::As(a, _, c) => {
|
|
||||||
TypeAnnotation::As(arena.alloc(a.remove_spaces(arena)), &[], c)
|
|
||||||
}
|
|
||||||
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
|
|
||||||
fields: fields.remove_spaces(arena),
|
|
||||||
ext: ext.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion {
|
|
||||||
ext: ext.remove_spaces(arena),
|
|
||||||
tags: tags.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
TypeAnnotation::Inferred => TypeAnnotation::Inferred,
|
|
||||||
TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
|
|
||||||
TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where(
|
|
||||||
arena.alloc(annot.remove_spaces(arena)),
|
|
||||||
arena.alloc(has_clauses.remove_spaces(arena)),
|
|
||||||
),
|
|
||||||
TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for HasClause<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
HasClause {
|
|
||||||
var: self.var.remove_spaces(arena),
|
|
||||||
ability: self.ability.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
|
||||||
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
|
||||||
match *self {
|
|
||||||
Tag::Apply { name, args } => Tag::Apply {
|
|
||||||
name: name.remove_spaces(arena),
|
|
||||||
args: args.remove_spaces(arena),
|
|
||||||
},
|
|
||||||
Tag::Malformed(a) => Tag::Malformed(a),
|
|
||||||
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
|
|
||||||
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
205
cli/src/lib.rs
205
cli/src/lib.rs
|
@ -3,16 +3,15 @@ extern crate const_format;
|
||||||
|
|
||||||
use build::BuiltFile;
|
use build::BuiltFile;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use clap::Command;
|
use clap::{Arg, ArgMatches, Command};
|
||||||
use clap::{Arg, ArgMatches};
|
|
||||||
use roc_build::link::LinkType;
|
use roc_build::link::LinkType;
|
||||||
use roc_error_macros::user_error;
|
use roc_error_macros::user_error;
|
||||||
use roc_load::{LoadingProblem, Threading};
|
use roc_load::{LoadingProblem, Threading};
|
||||||
use roc_mono::ir::OptLevel;
|
use roc_mono::ir::OptLevel;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process;
|
use std::process;
|
||||||
use target_lexicon::BinaryFormat;
|
use target_lexicon::BinaryFormat;
|
||||||
use target_lexicon::{
|
use target_lexicon::{
|
||||||
|
@ -35,6 +34,7 @@ pub const CMD_FORMAT: &str = "format";
|
||||||
pub const FLAG_DEBUG: &str = "debug";
|
pub const FLAG_DEBUG: &str = "debug";
|
||||||
pub const FLAG_DEV: &str = "dev";
|
pub const FLAG_DEV: &str = "dev";
|
||||||
pub const FLAG_OPTIMIZE: &str = "optimize";
|
pub const FLAG_OPTIMIZE: &str = "optimize";
|
||||||
|
pub const FLAG_MAX_THREADS: &str = "max-threads";
|
||||||
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
||||||
pub const FLAG_LIB: &str = "lib";
|
pub const FLAG_LIB: &str = "lib";
|
||||||
pub const FLAG_NO_LINK: &str = "no-link";
|
pub const FLAG_NO_LINK: &str = "no-link";
|
||||||
|
@ -58,6 +58,14 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.requires(ROC_FILE)
|
.requires(ROC_FILE)
|
||||||
.required(false);
|
.required(false);
|
||||||
|
|
||||||
|
let flag_max_threads = Arg::new(FLAG_MAX_THREADS)
|
||||||
|
.long(FLAG_MAX_THREADS)
|
||||||
|
.help("Limit the number of threads (and hence cores) used during compilation.")
|
||||||
|
.requires(ROC_FILE)
|
||||||
|
.takes_value(true)
|
||||||
|
.validator(|s| s.parse::<usize>())
|
||||||
|
.required(false);
|
||||||
|
|
||||||
let flag_opt_size = Arg::new(FLAG_OPT_SIZE)
|
let flag_opt_size = Arg::new(FLAG_OPT_SIZE)
|
||||||
.long(FLAG_OPT_SIZE)
|
.long(FLAG_OPT_SIZE)
|
||||||
.help("Optimize the compiled program to have a small binary size. (Optimization takes time to complete.)")
|
.help("Optimize the compiled program to have a small binary size. (Optimization takes time to complete.)")
|
||||||
|
@ -96,12 +104,23 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.possible_values(["true", "false"])
|
.possible_values(["true", "false"])
|
||||||
.required(false);
|
.required(false);
|
||||||
|
|
||||||
|
let roc_file_to_run = Arg::new(ROC_FILE)
|
||||||
|
.help("The .roc file of an app to run")
|
||||||
|
.allow_invalid_utf8(true);
|
||||||
|
|
||||||
|
let args_for_app = Arg::new(ARGS_FOR_APP)
|
||||||
|
.help("Arguments to pass into the app being run")
|
||||||
|
.requires(ROC_FILE)
|
||||||
|
.allow_invalid_utf8(true)
|
||||||
|
.multiple_values(true);
|
||||||
|
|
||||||
let app = Command::new("roc")
|
let app = Command::new("roc")
|
||||||
.version(concatcp!(VERSION, "\n"))
|
.version(concatcp!(VERSION, "\n"))
|
||||||
.about("Runs the given .roc file, if there are no compilation errors.\nUse one of the SUBCOMMANDS below to do something else!")
|
.about("Runs the given .roc file, if there are no compilation errors.\nUse one of the SUBCOMMANDS below to do something else!")
|
||||||
.subcommand(Command::new(CMD_BUILD)
|
.subcommand(Command::new(CMD_BUILD)
|
||||||
.about("Build a binary from the given .roc file, but don't run it")
|
.about("Build a binary from the given .roc file, but don't run it")
|
||||||
.arg(flag_optimize.clone())
|
.arg(flag_optimize.clone())
|
||||||
|
.arg(flag_max_threads.clone())
|
||||||
.arg(flag_opt_size.clone())
|
.arg(flag_opt_size.clone())
|
||||||
.arg(flag_dev.clone())
|
.arg(flag_dev.clone())
|
||||||
.arg(flag_debug.clone())
|
.arg(flag_debug.clone())
|
||||||
|
@ -132,6 +151,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(ROC_FILE)
|
Arg::new(ROC_FILE)
|
||||||
.help("The .roc file to build")
|
.help("The .roc file to build")
|
||||||
|
.allow_invalid_utf8(true)
|
||||||
.required(true),
|
.required(true),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -141,6 +161,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.subcommand(Command::new(CMD_RUN)
|
.subcommand(Command::new(CMD_RUN)
|
||||||
.about("Run a .roc file even if it has build errors")
|
.about("Run a .roc file even if it has build errors")
|
||||||
.arg(flag_optimize.clone())
|
.arg(flag_optimize.clone())
|
||||||
|
.arg(flag_max_threads.clone())
|
||||||
.arg(flag_opt_size.clone())
|
.arg(flag_opt_size.clone())
|
||||||
.arg(flag_dev.clone())
|
.arg(flag_dev.clone())
|
||||||
.arg(flag_debug.clone())
|
.arg(flag_debug.clone())
|
||||||
|
@ -148,11 +169,8 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.arg(flag_linker.clone())
|
.arg(flag_linker.clone())
|
||||||
.arg(flag_precompiled.clone())
|
.arg(flag_precompiled.clone())
|
||||||
.arg(flag_valgrind.clone())
|
.arg(flag_valgrind.clone())
|
||||||
.arg(
|
.arg(roc_file_to_run.clone().required(true))
|
||||||
Arg::new(ROC_FILE)
|
.arg(args_for_app.clone())
|
||||||
.help("The .roc file of an app to run")
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.subcommand(Command::new(CMD_FORMAT)
|
.subcommand(Command::new(CMD_FORMAT)
|
||||||
.about("Format a .roc file using standard Roc formatting")
|
.about("Format a .roc file using standard Roc formatting")
|
||||||
|
@ -174,9 +192,11 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.subcommand(Command::new(CMD_CHECK)
|
.subcommand(Command::new(CMD_CHECK)
|
||||||
.about("Check the code for problems, but doesn’t build or run it")
|
.about("Check the code for problems, but doesn’t build or run it")
|
||||||
.arg(flag_time.clone())
|
.arg(flag_time.clone())
|
||||||
|
.arg(flag_max_threads.clone())
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(ROC_FILE)
|
Arg::new(ROC_FILE)
|
||||||
.help("The .roc file of an app to check")
|
.help("The .roc file of an app to check")
|
||||||
|
.allow_invalid_utf8(true)
|
||||||
.required(true),
|
.required(true),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -193,6 +213,7 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
)
|
)
|
||||||
.trailing_var_arg(true)
|
.trailing_var_arg(true)
|
||||||
.arg(flag_optimize)
|
.arg(flag_optimize)
|
||||||
|
.arg(flag_max_threads.clone())
|
||||||
.arg(flag_opt_size)
|
.arg(flag_opt_size)
|
||||||
.arg(flag_dev)
|
.arg(flag_dev)
|
||||||
.arg(flag_debug)
|
.arg(flag_debug)
|
||||||
|
@ -200,17 +221,8 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
.arg(flag_linker)
|
.arg(flag_linker)
|
||||||
.arg(flag_precompiled)
|
.arg(flag_precompiled)
|
||||||
.arg(flag_valgrind)
|
.arg(flag_valgrind)
|
||||||
.arg(
|
.arg(roc_file_to_run.required(false))
|
||||||
Arg::new(ROC_FILE)
|
.arg(args_for_app);
|
||||||
.help("The .roc file of an app to build and run")
|
|
||||||
.required(false),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::new(ARGS_FOR_APP)
|
|
||||||
.help("Arguments to pass into the app being run")
|
|
||||||
.requires(ROC_FILE)
|
|
||||||
.multiple_values(true),
|
|
||||||
);
|
|
||||||
|
|
||||||
if cfg!(feature = "editor") {
|
if cfg!(feature = "editor") {
|
||||||
app.subcommand(
|
app.subcommand(
|
||||||
|
@ -236,8 +248,8 @@ pub fn docs(files: Vec<PathBuf>) {
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum BuildConfig {
|
pub enum BuildConfig {
|
||||||
BuildOnly,
|
BuildOnly,
|
||||||
BuildAndRun { roc_file_arg_index: usize },
|
BuildAndRun,
|
||||||
BuildAndRunIfNoErrors { roc_file_arg_index: usize },
|
BuildAndRunIfNoErrors,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FormatMode {
|
pub enum FormatMode {
|
||||||
|
@ -255,7 +267,7 @@ pub fn build(
|
||||||
use BuildConfig::*;
|
use BuildConfig::*;
|
||||||
|
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let filename = matches.value_of(ROC_FILE).unwrap();
|
let filename = matches.value_of_os(ROC_FILE).unwrap();
|
||||||
|
|
||||||
let original_cwd = std::env::current_dir()?;
|
let original_cwd = std::env::current_dir()?;
|
||||||
let opt_level = match (
|
let opt_level = match (
|
||||||
|
@ -272,6 +284,16 @@ pub fn build(
|
||||||
let emit_debug_info = matches.is_present(FLAG_DEBUG);
|
let emit_debug_info = matches.is_present(FLAG_DEBUG);
|
||||||
let emit_timings = matches.is_present(FLAG_TIME);
|
let emit_timings = matches.is_present(FLAG_TIME);
|
||||||
|
|
||||||
|
let threading = match matches
|
||||||
|
.value_of(FLAG_MAX_THREADS)
|
||||||
|
.and_then(|s| s.parse::<usize>().ok())
|
||||||
|
{
|
||||||
|
None => Threading::AllAvailable,
|
||||||
|
Some(0) => user_error!("cannot build with at most 0 threads"),
|
||||||
|
Some(1) => Threading::Single,
|
||||||
|
Some(n) => Threading::AtMost(n),
|
||||||
|
};
|
||||||
|
|
||||||
// Use surgical linking when supported, or when explicitly requested with --linker surgical
|
// Use surgical linking when supported, or when explicitly requested with --linker surgical
|
||||||
let surgically_link = if matches.is_present(FLAG_LINKER) {
|
let surgically_link = if matches.is_present(FLAG_LINKER) {
|
||||||
matches.value_of(FLAG_LINKER) == Some("surgical")
|
matches.value_of(FLAG_LINKER) == Some("surgical")
|
||||||
|
@ -321,7 +343,7 @@ pub fn build(
|
||||||
surgically_link,
|
surgically_link,
|
||||||
precompiled,
|
precompiled,
|
||||||
target_valgrind,
|
target_valgrind,
|
||||||
Threading::Multi,
|
threading,
|
||||||
);
|
);
|
||||||
|
|
||||||
match res_binary_path {
|
match res_binary_path {
|
||||||
|
@ -372,7 +394,7 @@ pub fn build(
|
||||||
// Return a nonzero exit code if there were problems
|
// Return a nonzero exit code if there were problems
|
||||||
Ok(problems.exit_code())
|
Ok(problems.exit_code())
|
||||||
}
|
}
|
||||||
BuildAndRun { roc_file_arg_index } => {
|
BuildAndRun => {
|
||||||
if problems.errors > 0 || problems.warnings > 0 {
|
if problems.errors > 0 || problems.warnings > 0 {
|
||||||
println!(
|
println!(
|
||||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m",
|
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nRunning program anyway…\n\n\x1B[36m{}\x1B[39m",
|
||||||
|
@ -403,15 +425,11 @@ pub fn build(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
roc_run(
|
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||||
arena,
|
|
||||||
&original_cwd,
|
roc_run(arena, &original_cwd, triple, args, &binary_path)
|
||||||
triple,
|
|
||||||
roc_file_arg_index,
|
|
||||||
&binary_path,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BuildAndRunIfNoErrors { roc_file_arg_index } => {
|
BuildAndRunIfNoErrors => {
|
||||||
if problems.errors == 0 {
|
if problems.errors == 0 {
|
||||||
if problems.warnings > 0 {
|
if problems.warnings > 0 {
|
||||||
println!(
|
println!(
|
||||||
|
@ -427,13 +445,9 @@ pub fn build(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
roc_run(
|
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||||
arena,
|
|
||||||
&original_cwd,
|
roc_run(arena, &original_cwd, triple, args, &binary_path)
|
||||||
triple,
|
|
||||||
roc_file_arg_index,
|
|
||||||
&binary_path,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
println!(
|
println!(
|
||||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with: \x1B[32mroc run {}\x1B[39m",
|
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with: \x1B[32mroc run {}\x1B[39m",
|
||||||
|
@ -460,7 +474,7 @@ pub fn build(
|
||||||
"warnings"
|
"warnings"
|
||||||
},
|
},
|
||||||
total_time.as_millis(),
|
total_time.as_millis(),
|
||||||
filename
|
filename.to_string_lossy()
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(problems.exit_code())
|
Ok(problems.exit_code())
|
||||||
|
@ -479,17 +493,14 @@ pub fn build(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
fn roc_run<'a, I: IntoIterator<Item = &'a OsStr>>(
|
||||||
fn roc_run(
|
|
||||||
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
triple: Triple,
|
triple: Triple,
|
||||||
roc_file_arg_index: usize,
|
args: I,
|
||||||
binary_path: &Path,
|
binary_path: &Path,
|
||||||
) -> io::Result<i32> {
|
) -> io::Result<i32> {
|
||||||
use std::os::unix::process::CommandExt;
|
match triple.architecture {
|
||||||
|
|
||||||
let mut cmd = match triple.architecture {
|
|
||||||
Architecture::Wasm32 => {
|
Architecture::Wasm32 => {
|
||||||
// If possible, report the generated executable name relative to the current dir.
|
// If possible, report the generated executable name relative to the current dir.
|
||||||
let generated_filename = binary_path
|
let generated_filename = binary_path
|
||||||
|
@ -500,19 +511,44 @@ fn roc_run(
|
||||||
// since the process is about to exit anyway.
|
// since the process is about to exit anyway.
|
||||||
std::mem::forget(arena);
|
std::mem::forget(arena);
|
||||||
|
|
||||||
let args = std::env::args()
|
if cfg!(target_family = "unix") {
|
||||||
.skip(roc_file_arg_index)
|
use std::os::unix::ffi::OsStrExt;
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
run_with_wasmer(generated_filename, &args);
|
run_with_wasmer(
|
||||||
return Ok(0);
|
generated_filename,
|
||||||
|
args.into_iter().map(|os_str| os_str.as_bytes()),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
run_with_wasmer(
|
||||||
|
generated_filename,
|
||||||
|
args.into_iter().map(|os_str| {
|
||||||
|
os_str.to_str().expect(
|
||||||
|
"Roc does not currently support passing non-UTF8 arguments to Wasmer.",
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if cfg!(target_family = "unix") {
|
||||||
|
roc_run_unix(cwd, args, binary_path)
|
||||||
|
} else {
|
||||||
|
roc_run_non_unix(arena, cwd, args, binary_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => std::process::Command::new(&binary_path),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Architecture::Wasm32 = triple.architecture {
|
|
||||||
cmd.arg(binary_path);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roc_run_unix<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||||
|
cwd: &Path,
|
||||||
|
args: I,
|
||||||
|
binary_path: &Path,
|
||||||
|
) -> io::Result<i32> {
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
|
||||||
|
let mut cmd = std::process::Command::new(&binary_path);
|
||||||
|
|
||||||
// Forward all the arguments after the .roc file argument
|
// Forward all the arguments after the .roc file argument
|
||||||
// to the new process. This way, you can do things like:
|
// to the new process. This way, you can do things like:
|
||||||
|
@ -521,10 +557,8 @@ fn roc_run(
|
||||||
//
|
//
|
||||||
// ...and have it so that app.roc will receive only `foo`,
|
// ...and have it so that app.roc will receive only `foo`,
|
||||||
// `bar`, and `baz` as its arguments.
|
// `bar`, and `baz` as its arguments.
|
||||||
for (index, arg) in std::env::args().enumerate() {
|
for arg in args {
|
||||||
if index > roc_file_arg_index {
|
cmd.arg(arg);
|
||||||
cmd.arg(arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is much faster than spawning a subprocess if we're on a UNIX system!
|
// This is much faster than spawning a subprocess if we're on a UNIX system!
|
||||||
|
@ -536,29 +570,36 @@ fn roc_run(
|
||||||
Err(err)
|
Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_family = "unix"))]
|
fn roc_run_non_unix<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(
|
||||||
fn roc_run(cmd: &mut Command) -> io::Result<i32> {
|
_arena: Bump, // This should be passed an owned value, not a reference, so we can usefully mem::forget it!
|
||||||
// Run the compiled app
|
_cwd: &Path,
|
||||||
let exit_status = cmd
|
_args: I,
|
||||||
.spawn()
|
_binary_path: &Path,
|
||||||
.unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
) -> io::Result<i32> {
|
||||||
.wait()
|
todo!("TODO support running roc programs on non-UNIX targets");
|
||||||
.expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app");
|
// let mut cmd = std::process::Command::new(&binary_path);
|
||||||
|
|
||||||
// `roc [FILE]` exits with the same status code as the app it ran.
|
// // Run the compiled app
|
||||||
//
|
// let exit_status = cmd
|
||||||
// If you want to know whether there were compilation problems
|
// .spawn()
|
||||||
// via status code, use either `roc build` or `roc check` instead!
|
// .unwrap_or_else(|err| panic!("Failed to run app after building it: {:?}", err))
|
||||||
match exit_status.code() {
|
// .wait()
|
||||||
Some(code) => Ok(code),
|
// .expect("TODO gracefully handle block_on failing when `roc` spawns a subprocess for the compiled app");
|
||||||
None => {
|
|
||||||
todo!("TODO gracefully handle the `roc [FILE]` subprocess terminating with a signal.");
|
// // `roc [FILE]` exits with the same status code as the app it ran.
|
||||||
}
|
// //
|
||||||
}
|
// // If you want to know whether there were compilation problems
|
||||||
|
// // via status code, use either `roc build` or `roc check` instead!
|
||||||
|
// match exit_status.code() {
|
||||||
|
// Some(code) => Ok(code),
|
||||||
|
// None => {
|
||||||
|
// todo!("TODO gracefully handle the `roc [FILE]` subprocess terminating with a signal.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "run-wasm32")]
|
#[cfg(feature = "run-wasm32")]
|
||||||
fn run_with_wasmer(wasm_path: &std::path::Path, args: &[String]) {
|
fn run_with_wasmer<I: Iterator<Item = S>, S: AsRef<[u8]>>(wasm_path: &std::path::Path, args: I) {
|
||||||
use wasmer::{Instance, Module, Store};
|
use wasmer::{Instance, Module, Store};
|
||||||
|
|
||||||
let store = Store::default();
|
let store = Store::default();
|
||||||
|
@ -589,8 +630,8 @@ fn run_with_wasmer(wasm_path: &std::path::Path, args: &[String]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "run-wasm32"))]
|
#[cfg(not(feature = "run-wasm32"))]
|
||||||
fn run_with_wasmer(_wasm_path: &std::path::Path, _args: &[String]) {
|
fn run_with_wasmer<I: Iterator<Item = S>, S: AsRef<[u8]>>(_wasm_path: &std::path::Path, _args: I) {
|
||||||
println!("Running wasm files not support");
|
println!("Running wasm files is not supported on this target.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
|
|
@ -6,7 +6,7 @@ use roc_cli::{
|
||||||
FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, ROC_FILE,
|
FLAG_NO_LINK, FLAG_TARGET, FLAG_TIME, ROC_FILE,
|
||||||
};
|
};
|
||||||
use roc_error_macros::user_error;
|
use roc_error_macros::user_error;
|
||||||
use roc_load::LoadingProblem;
|
use roc_load::{LoadingProblem, Threading};
|
||||||
use std::fs::{self, FileType};
|
use std::fs::{self, FileType};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -27,43 +27,31 @@ fn main() -> io::Result<()> {
|
||||||
|
|
||||||
let exit_code = match matches.subcommand() {
|
let exit_code = match matches.subcommand() {
|
||||||
None => {
|
None => {
|
||||||
match matches.index_of(ROC_FILE) {
|
if matches.is_present(ROC_FILE) {
|
||||||
Some(arg_index) => {
|
build(
|
||||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
&matches,
|
||||||
|
BuildConfig::BuildAndRunIfNoErrors,
|
||||||
|
Triple::host(),
|
||||||
|
LinkType::Executable,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
launch_editor(None)?;
|
||||||
|
|
||||||
build(
|
Ok(0)
|
||||||
&matches,
|
|
||||||
BuildConfig::BuildAndRunIfNoErrors { roc_file_arg_index },
|
|
||||||
Triple::host(),
|
|
||||||
LinkType::Executable,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
None => {
|
|
||||||
launch_editor(None)?;
|
|
||||||
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((CMD_RUN, matches)) => {
|
Some((CMD_RUN, matches)) => {
|
||||||
match matches.index_of(ROC_FILE) {
|
if matches.is_present(ROC_FILE) {
|
||||||
Some(arg_index) => {
|
build(
|
||||||
let roc_file_arg_index = arg_index + 1; // Not sure why this +1 is necessary, but it is!
|
matches,
|
||||||
|
BuildConfig::BuildAndRun,
|
||||||
|
Triple::host(),
|
||||||
|
LinkType::Executable,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
eprintln!("What .roc file do you want to run? Specify it at the end of the `roc run` command.");
|
||||||
|
|
||||||
build(
|
Ok(1)
|
||||||
matches,
|
|
||||||
BuildConfig::BuildAndRun { roc_file_arg_index },
|
|
||||||
Triple::host(),
|
|
||||||
LinkType::Executable,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
None => {
|
|
||||||
eprintln!("What .roc file do you want to run? Specify it at the end of the `roc run` command.");
|
|
||||||
|
|
||||||
Ok(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some((CMD_BUILD, matches)) => {
|
Some((CMD_BUILD, matches)) => {
|
||||||
|
@ -90,11 +78,21 @@ fn main() -> io::Result<()> {
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
|
|
||||||
let emit_timings = matches.is_present(FLAG_TIME);
|
let emit_timings = matches.is_present(FLAG_TIME);
|
||||||
let filename = matches.value_of(ROC_FILE).unwrap();
|
let filename = matches.value_of_os(ROC_FILE).unwrap();
|
||||||
let roc_file_path = PathBuf::from(filename);
|
let roc_file_path = PathBuf::from(filename);
|
||||||
let src_dir = roc_file_path.parent().unwrap().to_owned();
|
let src_dir = roc_file_path.parent().unwrap().to_owned();
|
||||||
|
|
||||||
match check_file(&arena, src_dir, roc_file_path, emit_timings) {
|
let threading = match matches
|
||||||
|
.value_of(roc_cli::FLAG_MAX_THREADS)
|
||||||
|
.and_then(|s| s.parse::<usize>().ok())
|
||||||
|
{
|
||||||
|
None => Threading::AllAvailable,
|
||||||
|
Some(0) => user_error!("cannot build with at most 0 threads"),
|
||||||
|
Some(1) => Threading::Single,
|
||||||
|
Some(n) => Threading::AtMost(n),
|
||||||
|
};
|
||||||
|
|
||||||
|
match check_file(&arena, src_dir, roc_file_path, emit_timings, threading) {
|
||||||
Ok((problems, total_time)) => {
|
Ok((problems, total_time)) => {
|
||||||
println!(
|
println!(
|
||||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.",
|
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.",
|
||||||
|
|
|
@ -14,10 +14,29 @@ mod cli_run {
|
||||||
known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError,
|
known_bad_file, run_cmd, run_roc, run_with_valgrind, Out, ValgrindError,
|
||||||
ValgrindErrorXWhat,
|
ValgrindErrorXWhat,
|
||||||
};
|
};
|
||||||
|
use const_format::concatcp;
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
use roc_cli::{CMD_BUILD, CMD_CHECK, CMD_FORMAT, CMD_RUN};
|
||||||
use roc_test_utils::assert_multiline_str_eq;
|
use roc_test_utils::assert_multiline_str_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
use std::iter;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
use strum_macros::EnumIter;
|
||||||
|
|
||||||
|
const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE);
|
||||||
|
const VALGRIND_FLAG: &str = concatcp!("--", roc_cli::FLAG_VALGRIND);
|
||||||
|
const LINKER_FLAG: &str = concatcp!("--", roc_cli::FLAG_LINKER);
|
||||||
|
const CHECK_FLAG: &str = concatcp!("--", roc_cli::FLAG_CHECK);
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const TARGET_FLAG: &str = concatcp!("--", roc_cli::FLAG_TARGET);
|
||||||
|
|
||||||
|
#[derive(Debug, EnumIter)]
|
||||||
|
enum CliMode {
|
||||||
|
RocBuild,
|
||||||
|
RocRun,
|
||||||
|
Roc,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
|
@ -65,7 +84,7 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) {
|
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) {
|
||||||
let compile_out = run_roc(&[&["check", file.to_str().unwrap()], flags].concat());
|
let compile_out = run_roc([CMD_CHECK, file.to_str().unwrap()].iter().chain(flags), &[]);
|
||||||
let err = compile_out.stdout.trim();
|
let err = compile_out.stdout.trim();
|
||||||
let err = strip_colors(err);
|
let err = strip_colors(err);
|
||||||
|
|
||||||
|
@ -77,19 +96,41 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
|
fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
|
||||||
let flags = &["--check"];
|
let out = run_roc([CMD_FORMAT, file.to_str().unwrap(), CHECK_FLAG], &[]);
|
||||||
let out = run_roc(&[&["format", file.to_str().unwrap()], &flags[..]].concat());
|
|
||||||
if expects_success_exit_code {
|
assert_eq!(out.status.success(), expects_success_exit_code);
|
||||||
assert!(out.status.success());
|
|
||||||
} else {
|
|
||||||
assert!(!out.status.success());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_example(file: &Path, flags: &[&str]) -> Out {
|
fn run_roc_on<'a, I: IntoIterator<Item = &'a str>>(
|
||||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
file: &'a Path,
|
||||||
if !compile_out.stderr.is_empty() {
|
args: I,
|
||||||
panic!("roc build had stderr: {}", compile_out.stderr);
|
stdin: &[&str],
|
||||||
|
input_file: Option<PathBuf>,
|
||||||
|
) -> Out {
|
||||||
|
let compile_out = match input_file {
|
||||||
|
Some(input_file) => run_roc(
|
||||||
|
// converting these all to String avoids lifetime issues
|
||||||
|
args.into_iter().map(|arg| arg.to_string()).chain([
|
||||||
|
file.to_str().unwrap().to_string(),
|
||||||
|
input_file.to_str().unwrap().to_string(),
|
||||||
|
]),
|
||||||
|
stdin,
|
||||||
|
),
|
||||||
|
None => run_roc(
|
||||||
|
args.into_iter().chain(iter::once(file.to_str().unwrap())),
|
||||||
|
stdin,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !compile_out.stderr.is_empty() &&
|
||||||
|
// If there is any stderr, it should be reporting the runtime and that's it!
|
||||||
|
!(compile_out.stderr.starts_with("runtime: ")
|
||||||
|
&& compile_out.stderr.ends_with("ms\n"))
|
||||||
|
{
|
||||||
|
panic!(
|
||||||
|
"`roc` command had unexpected stderr: {}",
|
||||||
|
compile_out.stderr
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(compile_out.status.success(), "bad status {:?}", compile_out);
|
assert!(compile_out.status.success(), "bad status {:?}", compile_out);
|
||||||
|
@ -106,86 +147,104 @@ mod cli_run {
|
||||||
expected_ending: &str,
|
expected_ending: &str,
|
||||||
use_valgrind: bool,
|
use_valgrind: bool,
|
||||||
) {
|
) {
|
||||||
let mut all_flags = vec![];
|
for cli_mode in CliMode::iter() {
|
||||||
all_flags.extend_from_slice(flags);
|
let flags = {
|
||||||
|
let mut vec = flags.to_vec();
|
||||||
|
|
||||||
if use_valgrind {
|
if use_valgrind {
|
||||||
all_flags.extend_from_slice(&["--valgrind"]);
|
vec.push(VALGRIND_FLAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
build_example(file, &all_flags[..]);
|
vec.into_iter()
|
||||||
|
|
||||||
let out = if use_valgrind && ALLOW_VALGRIND {
|
|
||||||
let (valgrind_out, raw_xml) = if let Some(input_file) = input_file {
|
|
||||||
run_with_valgrind(
|
|
||||||
stdin,
|
|
||||||
&[
|
|
||||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
|
||||||
input_file.to_str().unwrap(),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
run_with_valgrind(
|
|
||||||
stdin,
|
|
||||||
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if valgrind_out.status.success() {
|
let out = match cli_mode {
|
||||||
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
CliMode::RocBuild => {
|
||||||
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr);
|
run_roc_on(file, iter::once(CMD_BUILD).chain(flags.clone()), &[], None);
|
||||||
});
|
|
||||||
|
|
||||||
if !memory_errors.is_empty() {
|
if use_valgrind && ALLOW_VALGRIND {
|
||||||
for error in memory_errors {
|
let (valgrind_out, raw_xml) = if let Some(ref input_file) = input_file {
|
||||||
let ValgrindError {
|
run_with_valgrind(
|
||||||
kind,
|
stdin.clone().iter().copied(),
|
||||||
what: _,
|
&[
|
||||||
xwhat,
|
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||||
} = error;
|
input_file.clone().to_str().unwrap(),
|
||||||
println!("Valgrind Error: {}\n", kind);
|
],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
run_with_valgrind(
|
||||||
|
stdin.clone().iter().copied(),
|
||||||
|
&[file.with_file_name(executable_filename).to_str().unwrap()],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(ValgrindErrorXWhat {
|
if valgrind_out.status.success() {
|
||||||
text,
|
let memory_errors = extract_valgrind_errors(&raw_xml).unwrap_or_else(|err| {
|
||||||
leakedbytes: _,
|
panic!("failed to parse the `valgrind` xml output. Error was:\n\n{:?}\n\nvalgrind xml was: \"{}\"\n\nvalgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", err, raw_xml, valgrind_out.stdout, valgrind_out.stderr);
|
||||||
leakedblocks: _,
|
});
|
||||||
}) = xwhat
|
|
||||||
{
|
if !memory_errors.is_empty() {
|
||||||
println!(" {}", text);
|
for error in memory_errors {
|
||||||
|
let ValgrindError {
|
||||||
|
kind,
|
||||||
|
what: _,
|
||||||
|
xwhat,
|
||||||
|
} = error;
|
||||||
|
println!("Valgrind Error: {}\n", kind);
|
||||||
|
|
||||||
|
if let Some(ValgrindErrorXWhat {
|
||||||
|
text,
|
||||||
|
leakedbytes: _,
|
||||||
|
leakedblocks: _,
|
||||||
|
}) = xwhat
|
||||||
|
{
|
||||||
|
println!(" {}", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Valgrind reported memory errors");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let exit_code = match valgrind_out.status.code() {
|
||||||
|
Some(code) => format!("exit code {}", code),
|
||||||
|
None => "no exit code".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
panic!("`valgrind` exited with {}. valgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", exit_code, valgrind_out.stdout, valgrind_out.stderr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
panic!("Valgrind reported memory errors");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let exit_code = match valgrind_out.status.code() {
|
|
||||||
Some(code) => format!("exit code {}", code),
|
|
||||||
None => "no exit code".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
panic!("`valgrind` exited with {}. valgrind stdout was: \"{}\"\n\nvalgrind stderr was: \"{}\"", exit_code, valgrind_out.stdout, valgrind_out.stderr);
|
valgrind_out
|
||||||
|
} else if let Some(ref input_file) = input_file {
|
||||||
|
run_cmd(
|
||||||
|
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||||
|
stdin.iter().copied(),
|
||||||
|
&[input_file.to_str().unwrap()],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
run_cmd(
|
||||||
|
file.with_file_name(executable_filename).to_str().unwrap(),
|
||||||
|
stdin.iter().copied(),
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CliMode::Roc => run_roc_on(file, flags.clone(), stdin, input_file.clone()),
|
||||||
|
CliMode::RocRun => run_roc_on(
|
||||||
|
file,
|
||||||
|
iter::once(CMD_RUN).chain(flags.clone()),
|
||||||
|
stdin,
|
||||||
|
input_file.clone(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !&out.stdout.ends_with(expected_ending) {
|
||||||
|
panic!(
|
||||||
|
"expected output to end with {:?} but instead got {:#?} - stderr was: {:#?}",
|
||||||
|
expected_ending, out.stdout, out.stderr
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
valgrind_out
|
assert!(out.status.success());
|
||||||
} else if let Some(input_file) = input_file {
|
|
||||||
run_cmd(
|
|
||||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
|
||||||
stdin,
|
|
||||||
&[input_file.to_str().unwrap()],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
run_cmd(
|
|
||||||
file.with_file_name(executable_filename).to_str().unwrap(),
|
|
||||||
stdin,
|
|
||||||
&[],
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if !&out.stdout.ends_with(expected_ending) {
|
|
||||||
panic!(
|
|
||||||
"expected output to end with {:?} but instead got {:#?} - stderr was: {:#?}",
|
|
||||||
expected_ending, out.stdout, out.stderr
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
assert!(out.status.success());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "wasm32-cli-run")]
|
#[cfg(feature = "wasm32-cli-run")]
|
||||||
|
@ -199,9 +258,13 @@ mod cli_run {
|
||||||
) {
|
) {
|
||||||
assert_eq!(input_file, None, "Wasm does not support input files");
|
assert_eq!(input_file, None, "Wasm does not support input files");
|
||||||
let mut flags = flags.to_vec();
|
let mut flags = flags.to_vec();
|
||||||
flags.push("--target=wasm32");
|
flags.push(concatcp!(TARGET_FLAG, "=wasm32"));
|
||||||
|
|
||||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
let compile_out = run_roc(
|
||||||
|
[CMD_BUILD, file.to_str().unwrap()]
|
||||||
|
.iter()
|
||||||
|
.chain(flags.as_slice()),
|
||||||
|
);
|
||||||
if !compile_out.stderr.is_empty() {
|
if !compile_out.stderr.is_empty() {
|
||||||
panic!("{}", compile_out.stderr);
|
panic!("{}", compile_out.stderr);
|
||||||
}
|
}
|
||||||
|
@ -258,7 +321,7 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
"hello-gui" | "breakout" => {
|
"hello-gui" | "breakout" => {
|
||||||
// Since these require opening a window, we do `roc build` on them but don't run them.
|
// Since these require opening a window, we do `roc build` on them but don't run them.
|
||||||
build_example(&file_name, &["--optimize"]);
|
run_roc_on(&file_name, [CMD_BUILD, OPTIMIZE_FLAG], &[], None);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -283,7 +346,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
example.stdin,
|
example.stdin,
|
||||||
example.executable_filename,
|
example.executable_filename,
|
||||||
&["--optimize"],
|
&[OPTIMIZE_FLAG],
|
||||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||||
example.expected_ending,
|
example.expected_ending,
|
||||||
example.use_valgrind,
|
example.use_valgrind,
|
||||||
|
@ -296,7 +359,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
example.stdin,
|
example.stdin,
|
||||||
example.executable_filename,
|
example.executable_filename,
|
||||||
&["--linker", "legacy"],
|
&[LINKER_FLAG, "legacy"],
|
||||||
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
example.input_file.and_then(|file| Some(example_file(dir_name, file))),
|
||||||
example.expected_ending,
|
example.expected_ending,
|
||||||
example.use_valgrind,
|
example.use_valgrind,
|
||||||
|
@ -513,7 +576,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
benchmark.stdin,
|
benchmark.stdin,
|
||||||
benchmark.executable_filename,
|
benchmark.executable_filename,
|
||||||
&["--optimize"],
|
&[OPTIMIZE_FLAG],
|
||||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||||
benchmark.expected_ending,
|
benchmark.expected_ending,
|
||||||
benchmark.use_valgrind,
|
benchmark.use_valgrind,
|
||||||
|
@ -555,7 +618,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
benchmark.stdin,
|
benchmark.stdin,
|
||||||
benchmark.executable_filename,
|
benchmark.executable_filename,
|
||||||
&["--optimize"],
|
&[OPTIMIZE_FLAG],
|
||||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||||
benchmark.expected_ending,
|
benchmark.expected_ending,
|
||||||
);
|
);
|
||||||
|
@ -587,7 +650,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
benchmark.stdin,
|
benchmark.stdin,
|
||||||
benchmark.executable_filename,
|
benchmark.executable_filename,
|
||||||
&["--target=x86_32"],
|
[concatcp!(TARGET_FLAG, "=x86_32")],
|
||||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||||
benchmark.expected_ending,
|
benchmark.expected_ending,
|
||||||
benchmark.use_valgrind,
|
benchmark.use_valgrind,
|
||||||
|
@ -597,7 +660,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
benchmark.stdin,
|
benchmark.stdin,
|
||||||
benchmark.executable_filename,
|
benchmark.executable_filename,
|
||||||
&["--target=x86_32", "--optimize"],
|
[concatcp!(TARGET_FLAG, "=x86_32"), OPTIMIZE_FLAG],
|
||||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||||
benchmark.expected_ending,
|
benchmark.expected_ending,
|
||||||
benchmark.use_valgrind,
|
benchmark.use_valgrind,
|
||||||
|
@ -816,7 +879,7 @@ mod cli_run {
|
||||||
&fixture_file("multi-dep-str", "Main.roc"),
|
&fixture_file("multi-dep-str", "Main.roc"),
|
||||||
&[],
|
&[],
|
||||||
"multi-dep-str",
|
"multi-dep-str",
|
||||||
&["--optimize"],
|
&[OPTIMIZE_FLAG],
|
||||||
None,
|
None,
|
||||||
"I am Dep2.str2\n",
|
"I am Dep2.str2\n",
|
||||||
true,
|
true,
|
||||||
|
@ -844,7 +907,7 @@ mod cli_run {
|
||||||
&fixture_file("multi-dep-thunk", "Main.roc"),
|
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||||
&[],
|
&[],
|
||||||
"multi-dep-thunk",
|
"multi-dep-thunk",
|
||||||
&["--optimize"],
|
&[OPTIMIZE_FLAG],
|
||||||
None,
|
None,
|
||||||
"I am Dep2.value2\n",
|
"I am Dep2.value2\n",
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -20,6 +20,7 @@ serde = { version = "1.0.130", features = ["derive"] }
|
||||||
serde-xml-rs = "0.5.1"
|
serde-xml-rs = "0.5.1"
|
||||||
strip-ansi-escapes = "0.1.1"
|
strip-ansi-escapes = "0.1.1"
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
|
const_format = "0.2.22"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
rlimit = "0.6.2"
|
rlimit = "0.6.2"
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
use crate::helpers::{example_file, run_cmd, run_roc};
|
use crate::helpers::{example_file, run_cmd, run_roc};
|
||||||
|
use const_format::concatcp;
|
||||||
use criterion::{black_box, measurement::Measurement, BenchmarkGroup};
|
use criterion::{black_box, measurement::Measurement, BenchmarkGroup};
|
||||||
|
use roc_cli::CMD_BUILD;
|
||||||
use std::{path::Path, thread};
|
use std::{path::Path, thread};
|
||||||
|
|
||||||
const CFOLD_STACK_SIZE: usize = 8192 * 100000;
|
const CFOLD_STACK_SIZE: usize = 8192 * 100000;
|
||||||
|
|
||||||
|
const OPTIMIZE_FLAG: &str = concatcp!("--", roc_cli::FLAG_OPTIMIZE);
|
||||||
|
|
||||||
fn exec_bench_w_input<T: Measurement>(
|
fn exec_bench_w_input<T: Measurement>(
|
||||||
file: &Path,
|
file: &Path,
|
||||||
stdin_str: &'static str,
|
stdin_str: &'static str,
|
||||||
|
@ -11,9 +15,10 @@ fn exec_bench_w_input<T: Measurement>(
|
||||||
expected_ending: &str,
|
expected_ending: &str,
|
||||||
bench_group_opt: Option<&mut BenchmarkGroup<T>>,
|
bench_group_opt: Option<&mut BenchmarkGroup<T>>,
|
||||||
) {
|
) {
|
||||||
let flags: &[&str] = &["--optimize"];
|
let compile_out = run_roc(
|
||||||
|
[CMD_BUILD, OPTIMIZE_FLAG, file.to_str().unwrap()],
|
||||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags].concat());
|
&[stdin_str],
|
||||||
|
);
|
||||||
|
|
||||||
if !compile_out.stderr.is_empty() {
|
if !compile_out.stderr.is_empty() {
|
||||||
panic!("{}", compile_out.stderr);
|
panic!("{}", compile_out.stderr);
|
||||||
|
@ -45,12 +50,12 @@ fn check_cmd_output(
|
||||||
let out = if cmd_str.contains("cfold") {
|
let out = if cmd_str.contains("cfold") {
|
||||||
let child = thread::Builder::new()
|
let child = thread::Builder::new()
|
||||||
.stack_size(CFOLD_STACK_SIZE)
|
.stack_size(CFOLD_STACK_SIZE)
|
||||||
.spawn(move || run_cmd(&cmd_str, &[stdin_str], &[]))
|
.spawn(move || run_cmd(&cmd_str, [stdin_str], &[]))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
child.join().unwrap()
|
child.join().unwrap()
|
||||||
} else {
|
} else {
|
||||||
run_cmd(&cmd_str, &[stdin_str], &[])
|
run_cmd(&cmd_str, [stdin_str], &[])
|
||||||
};
|
};
|
||||||
|
|
||||||
if !&out.stdout.ends_with(expected_ending) {
|
if !&out.stdout.ends_with(expected_ending) {
|
||||||
|
@ -93,12 +98,12 @@ fn bench_cmd<T: Measurement>(
|
||||||
|
|
||||||
if let Some(bench_group) = bench_group_opt {
|
if let Some(bench_group) = bench_group_opt {
|
||||||
bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| {
|
bench_group.bench_function(&format!("Benchmarking {:?}", executable_filename), |b| {
|
||||||
b.iter(|| run_cmd(black_box(&cmd_str), black_box(&[stdin_str]), &[]))
|
b.iter(|| run_cmd(black_box(&cmd_str), black_box([stdin_str]), &[]))
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
run_cmd(
|
run_cmd(
|
||||||
black_box(file.with_file_name(executable_filename).to_str().unwrap()),
|
black_box(file.with_file_name(executable_filename).to_str().unwrap()),
|
||||||
black_box(&[stdin_str]),
|
black_box([stdin_str]),
|
||||||
&[],
|
&[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ extern crate tempfile;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_xml_rs::from_str;
|
use serde_xml_rs::from_str;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -44,18 +45,34 @@ pub fn path_to_roc_binary() -> PathBuf {
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn run_roc<I: IntoIterator<Item = S>, S: AsRef<OsStr>>(args: I, stdin_vals: &[&str]) -> Out {
|
||||||
pub fn run_roc(args: &[&str]) -> Out {
|
|
||||||
let mut cmd = Command::new(path_to_roc_binary());
|
let mut cmd = Command::new(path_to_roc_binary());
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
cmd.arg(arg);
|
cmd.arg(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = cmd
|
let mut child = cmd
|
||||||
.output()
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
.expect("failed to execute compiled `roc` binary in CLI test");
|
.expect("failed to execute compiled `roc` binary in CLI test");
|
||||||
|
|
||||||
|
{
|
||||||
|
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
||||||
|
|
||||||
|
for stdin_str in stdin_vals.iter() {
|
||||||
|
stdin
|
||||||
|
.write_all(stdin_str.as_bytes())
|
||||||
|
.expect("Failed to write to stdin");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = child
|
||||||
|
.wait_with_output()
|
||||||
|
.expect("failed to get output for compiled `roc` binary in CLI test");
|
||||||
|
|
||||||
Out {
|
Out {
|
||||||
stdout: String::from_utf8(output.stdout).unwrap(),
|
stdout: String::from_utf8(output.stdout).unwrap(),
|
||||||
stderr: String::from_utf8(output.stderr).unwrap(),
|
stderr: String::from_utf8(output.stderr).unwrap(),
|
||||||
|
@ -63,8 +80,11 @@ pub fn run_roc(args: &[&str]) -> Out {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn run_cmd<'a, I: IntoIterator<Item = &'a str>>(
|
||||||
pub fn run_cmd(cmd_name: &str, stdin_vals: &[&str], args: &[&str]) -> Out {
|
cmd_name: &str,
|
||||||
|
stdin_vals: I,
|
||||||
|
args: &[&str],
|
||||||
|
) -> Out {
|
||||||
let mut cmd = Command::new(cmd_name);
|
let mut cmd = Command::new(cmd_name);
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
|
@ -99,8 +119,10 @@ pub fn run_cmd(cmd_name: &str, stdin_vals: &[&str], args: &[&str]) -> Out {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
pub fn run_with_valgrind<'a, I: IntoIterator<Item = &'a str>>(
|
||||||
pub fn run_with_valgrind(stdin_vals: &[&str], args: &[&str]) -> (Out, String) {
|
stdin_vals: I,
|
||||||
|
args: &[&str],
|
||||||
|
) -> (Out, String) {
|
||||||
//TODO: figure out if there is a better way to get the valgrind executable.
|
//TODO: figure out if there is a better way to get the valgrind executable.
|
||||||
let mut cmd = Command::new("valgrind");
|
let mut cmd = Command::new("valgrind");
|
||||||
let named_tempfile =
|
let named_tempfile =
|
||||||
|
|
|
@ -106,6 +106,9 @@ comptime {
|
||||||
num.exportToIntCheckingMax(FROM, TO, ROC_BUILTINS ++ "." ++ NUM ++ ".int_to_" ++ @typeName(TO) ++ "_checking_max.");
|
num.exportToIntCheckingMax(FROM, TO, ROC_BUILTINS ++ "." ++ NUM ++ ".int_to_" ++ @typeName(TO) ++ "_checking_max.");
|
||||||
num.exportToIntCheckingMaxAndMin(FROM, TO, ROC_BUILTINS ++ "." ++ NUM ++ ".int_to_" ++ @typeName(TO) ++ "_checking_max_and_min.");
|
num.exportToIntCheckingMaxAndMin(FROM, TO, ROC_BUILTINS ++ "." ++ NUM ++ ".int_to_" ++ @typeName(TO) ++ "_checking_max_and_min.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
num.exportRoundF32(FROM, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f32.");
|
||||||
|
num.exportRoundF64(FROM, ROC_BUILTINS ++ "." ++ NUM ++ ".round_f64.");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (FLOATS) |T| {
|
inline for (FLOATS) |T| {
|
||||||
|
@ -114,7 +117,6 @@ comptime {
|
||||||
num.exportAtan(T, ROC_BUILTINS ++ "." ++ NUM ++ ".atan.");
|
num.exportAtan(T, ROC_BUILTINS ++ "." ++ NUM ++ ".atan.");
|
||||||
|
|
||||||
num.exportIsFinite(T, ROC_BUILTINS ++ "." ++ NUM ++ ".is_finite.");
|
num.exportIsFinite(T, ROC_BUILTINS ++ "." ++ NUM ++ ".is_finite.");
|
||||||
num.exportRound(T, ROC_BUILTINS ++ "." ++ NUM ++ ".round.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,10 +90,19 @@ pub fn exportAtan(comptime T: type, comptime name: []const u8) void {
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exportRound(comptime T: type, comptime name: []const u8) void {
|
pub fn exportRoundF32(comptime T: type, comptime name: []const u8) void {
|
||||||
comptime var f = struct {
|
comptime var f = struct {
|
||||||
fn func(input: T) callconv(.C) i64 {
|
fn func(input: f32) callconv(.C) T {
|
||||||
return @floatToInt(i64, (@round(input)));
|
return @floatToInt(T, (@round(input)));
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportRoundF64(comptime T: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(input: f64) callconv(.C) T {
|
||||||
|
return @floatToInt(T, (@round(input)));
|
||||||
}
|
}
|
||||||
}.func;
|
}.func;
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
|
|
|
@ -279,7 +279,9 @@ pub const NUM_ATAN: IntrinsicName = float_intrinsic!("roc_builtins.num.atan");
|
||||||
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
|
pub const NUM_IS_FINITE: IntrinsicName = float_intrinsic!("roc_builtins.num.is_finite");
|
||||||
pub const NUM_POW_INT: IntrinsicName = int_intrinsic!("roc_builtins.num.pow_int");
|
pub const NUM_POW_INT: IntrinsicName = int_intrinsic!("roc_builtins.num.pow_int");
|
||||||
pub const NUM_DIV_CEIL: IntrinsicName = int_intrinsic!("roc_builtins.num.div_ceil");
|
pub const NUM_DIV_CEIL: IntrinsicName = int_intrinsic!("roc_builtins.num.div_ceil");
|
||||||
pub const NUM_ROUND: IntrinsicName = float_intrinsic!("roc_builtins.num.round");
|
|
||||||
|
pub const NUM_ROUND_F32: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f32");
|
||||||
|
pub const NUM_ROUND_F64: IntrinsicName = int_intrinsic!("roc_builtins.num.round_f64");
|
||||||
|
|
||||||
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
|
||||||
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
|
||||||
|
|
|
@ -14,8 +14,6 @@ roc_module = { path = "../module" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_problem = { path = "../problem" }
|
roc_problem = { path = "../problem" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_builtins = { path = "../builtins" }
|
|
||||||
ven_graph = { path = "../../vendor/pathfinding" }
|
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
bitvec = "1"
|
bitvec = "1"
|
||||||
|
|
|
@ -192,17 +192,12 @@ fn sort_type_defs_before_introduction(
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the strongly connected components and their relations
|
// find the strongly connected components and their relations
|
||||||
let nodes: Vec<_> = (0..capacity as u32).collect();
|
matrix
|
||||||
|
.strongly_connected_components_all()
|
||||||
let mut output = Vec::with_capacity(capacity);
|
.groups()
|
||||||
|
.flat_map(|group| group.iter_ones())
|
||||||
for group in matrix.strongly_connected_components(&nodes).groups() {
|
.map(|index| symbols[index])
|
||||||
for index in group.iter_ones() {
|
.collect()
|
||||||
output.push(symbols[index])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -790,14 +785,10 @@ pub(crate) fn sort_can_defs(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let nodes: Vec<_> = (0..defs.len() as u32).collect();
|
|
||||||
|
|
||||||
// We first perform SCC based on any reference, both variable usage and calls
|
// We first perform SCC based on any reference, both variable usage and calls
|
||||||
// considering both value definitions and function bodies. This will spot any
|
// considering both value definitions and function bodies. This will spot any
|
||||||
// recursive relations between any 2 definitions.
|
// recursive relations between any 2 definitions.
|
||||||
let sccs = def_ordering
|
let sccs = def_ordering.references.strongly_connected_components_all();
|
||||||
.references
|
|
||||||
.strongly_connected_components(&nodes);
|
|
||||||
|
|
||||||
let mut declarations = Vec::new();
|
let mut declarations = Vec::new();
|
||||||
|
|
||||||
|
@ -838,10 +829,9 @@ pub(crate) fn sort_can_defs(
|
||||||
// boom = \{} -> boom {}
|
// boom = \{} -> boom {}
|
||||||
//
|
//
|
||||||
// In general we cannot spot faulty recursion (halting problem) so this is our best attempt
|
// In general we cannot spot faulty recursion (halting problem) so this is our best attempt
|
||||||
let nodes: Vec<_> = group.iter_ones().map(|v| v as u32).collect();
|
|
||||||
let direct_sccs = def_ordering
|
let direct_sccs = def_ordering
|
||||||
.direct_references
|
.direct_references
|
||||||
.strongly_connected_components(&nodes);
|
.strongly_connected_components_subset(group);
|
||||||
|
|
||||||
let declaration = if direct_sccs.groups().count() == 1 {
|
let declaration = if direct_sccs.groups().count() == 1 {
|
||||||
// all defs are part of the same direct cycle, that is invalid!
|
// all defs are part of the same direct cycle, that is invalid!
|
||||||
|
@ -1571,8 +1561,7 @@ fn correct_mutual_recursive_type_alias<'a>(
|
||||||
|
|
||||||
let mut solved_aliases = bitvec::vec::BitVec::<usize>::repeat(false, capacity);
|
let mut solved_aliases = bitvec::vec::BitVec::<usize>::repeat(false, capacity);
|
||||||
|
|
||||||
let group: Vec<_> = (0u32..capacity as u32).collect();
|
let sccs = matrix.strongly_connected_components_all();
|
||||||
let sccs = matrix.strongly_connected_components(&group);
|
|
||||||
|
|
||||||
// scratchpad to store aliases that are modified in the current iteration.
|
// scratchpad to store aliases that are modified in the current iteration.
|
||||||
// Only used when there is are more than one alias in a group. See below why
|
// Only used when there is are more than one alias in a group. See below why
|
||||||
|
|
|
@ -129,8 +129,14 @@ impl ReferenceMatrix {
|
||||||
TopologicalSort::Groups { groups }
|
TopologicalSort::Groups { groups }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the strongly-connected components of the set of input nodes.
|
/// Get the strongly-connected components all nodes in the matrix
|
||||||
pub fn strongly_connected_components(&self, nodes: &[u32]) -> Sccs {
|
pub fn strongly_connected_components_all(&self) -> Sccs {
|
||||||
|
let bitvec = BitVec::repeat(true, self.length);
|
||||||
|
self.strongly_connected_components_subset(&bitvec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the strongly-connected components of a set of input nodes.
|
||||||
|
pub fn strongly_connected_components_subset(&self, nodes: &BitSlice) -> Sccs {
|
||||||
let mut params = Params::new(self.length, nodes);
|
let mut params = Params::new(self.length, nodes);
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
|
@ -176,15 +182,15 @@ struct Params {
|
||||||
p: Vec<u32>,
|
p: Vec<u32>,
|
||||||
s: Vec<u32>,
|
s: Vec<u32>,
|
||||||
scc: Sccs,
|
scc: Sccs,
|
||||||
scca: Vec<u32>,
|
scca: BitVec,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Params {
|
impl Params {
|
||||||
fn new(length: usize, group: &[u32]) -> Self {
|
fn new(length: usize, group: &BitSlice) -> Self {
|
||||||
let mut preorders = vec![Preorder::Removed; length];
|
let mut preorders = vec![Preorder::Removed; length];
|
||||||
|
|
||||||
for value in group {
|
for index in group.iter_ones() {
|
||||||
preorders[*value as usize] = Preorder::Empty;
|
preorders[index] = Preorder::Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -196,7 +202,7 @@ impl Params {
|
||||||
matrix: ReferenceMatrix::new(length),
|
matrix: ReferenceMatrix::new(length),
|
||||||
components: 0,
|
components: 0,
|
||||||
},
|
},
|
||||||
scca: Vec::new(),
|
scca: BitVec::repeat(false, length),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +216,7 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
|
||||||
params.p.push(v as u32);
|
params.p.push(v as u32);
|
||||||
|
|
||||||
for w in bitvec[v * length..][..length].iter_ones() {
|
for w in bitvec[v * length..][..length].iter_ones() {
|
||||||
if !params.scca.contains(&(w as u32)) {
|
if !params.scca[w] {
|
||||||
match params.preorders[w] {
|
match params.preorders[w] {
|
||||||
Preorder::Filled(pw) => loop {
|
Preorder::Filled(pw) => loop {
|
||||||
let index = *params.p.last().unwrap();
|
let index = *params.p.last().unwrap();
|
||||||
|
@ -241,7 +247,7 @@ fn recurse_onto(length: usize, bitvec: &BitVec, v: usize, params: &mut Params) {
|
||||||
.scc
|
.scc
|
||||||
.matrix
|
.matrix
|
||||||
.set_row_col(params.scc.components, node as usize, true);
|
.set_row_col(params.scc.components, node as usize, true);
|
||||||
params.scca.push(node);
|
params.scca.set(node as usize, true);
|
||||||
params.preorders[node as usize] = Preorder::Removed;
|
params.preorders[node as usize] = Preorder::Removed;
|
||||||
if node as usize == v {
|
if node as usize == v {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -13,6 +13,13 @@ impl<K, V> Default for VecMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K, V> VecMap<K, V> {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
debug_assert_eq!(self.keys.len(), self.values.len());
|
||||||
|
self.keys.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<K: PartialEq, V> VecMap<K, V> {
|
impl<K: PartialEq, V> VecMap<K, V> {
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -21,11 +28,6 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
debug_assert_eq!(self.keys.len(), self.values.len());
|
|
||||||
self.keys.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
debug_assert_eq!(self.keys.len(), self.values.len());
|
debug_assert_eq!(self.keys.len(), self.values.len());
|
||||||
self.keys.is_empty()
|
self.keys.is_empty()
|
||||||
|
@ -58,15 +60,9 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
||||||
self.keys.contains(key)
|
self.keys.contains(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(&mut self, key: &K) {
|
pub fn remove(&mut self, key: &K) -> Option<(K, V)> {
|
||||||
match self.keys.iter().position(|x| x == key) {
|
let index = self.keys.iter().position(|x| x == key)?;
|
||||||
None => {
|
Some(self.swap_remove(index))
|
||||||
// just do nothing
|
|
||||||
}
|
|
||||||
Some(index) => {
|
|
||||||
self.swap_remove(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, key: &K) -> Option<&V> {
|
pub fn get(&self, key: &K) -> Option<&V> {
|
||||||
|
@ -83,7 +79,7 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_or_insert(&mut self, key: K, default_value: impl Fn() -> V) -> &mut V {
|
pub fn get_or_insert(&mut self, key: K, default_value: impl FnOnce() -> V) -> &mut V {
|
||||||
match self.keys.iter().position(|x| x == &key) {
|
match self.keys.iter().position(|x| x == &key) {
|
||||||
Some(index) => &mut self.values[index],
|
Some(index) => &mut self.values[index],
|
||||||
None => {
|
None => {
|
||||||
|
@ -97,15 +93,15 @@ impl<K: PartialEq, V> VecMap<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
|
pub fn iter(&self) -> impl ExactSizeIterator<Item = (&K, &V)> {
|
||||||
self.keys.iter().zip(self.values.iter())
|
self.keys.iter().zip(self.values.iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn keys(&self) -> impl Iterator<Item = &K> {
|
pub fn keys(&self) -> impl ExactSizeIterator<Item = &K> {
|
||||||
self.keys.iter()
|
self.keys.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn values(&self) -> impl Iterator<Item = &V> {
|
pub fn values(&self) -> impl ExactSizeIterator<Item = &V> {
|
||||||
self.values.iter()
|
self.values.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +155,7 @@ impl<K, V> IntoIterator for VecMap<K, V> {
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
IntoIter {
|
IntoIter {
|
||||||
|
len: self.len(),
|
||||||
keys: self.keys.into_iter(),
|
keys: self.keys.into_iter(),
|
||||||
values: self.values.into_iter(),
|
values: self.values.into_iter(),
|
||||||
}
|
}
|
||||||
|
@ -166,6 +163,7 @@ impl<K, V> IntoIterator for VecMap<K, V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IntoIter<K, V> {
|
pub struct IntoIter<K, V> {
|
||||||
|
len: usize,
|
||||||
keys: std::vec::IntoIter<K>,
|
keys: std::vec::IntoIter<K>,
|
||||||
values: std::vec::IntoIter<V>,
|
values: std::vec::IntoIter<V>,
|
||||||
}
|
}
|
||||||
|
@ -180,3 +178,9 @@ impl<K, V> Iterator for IntoIter<K, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K, V> ExactSizeIterator for IntoIter<K, V> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,14 @@ pub mod pattern;
|
||||||
pub mod spaces;
|
pub mod spaces;
|
||||||
|
|
||||||
use bumpalo::{collections::String, Bump};
|
use bumpalo::{collections::String, Bump};
|
||||||
|
use roc_parse::ast::{Def, Module};
|
||||||
|
use roc_region::all::Loc;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Ast<'a> {
|
||||||
|
pub module: Module<'a>,
|
||||||
|
pub defs: bumpalo::collections::vec::Vec<'a, Loc<Def<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Buf<'a> {
|
pub struct Buf<'a> {
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
use roc_parse::ast::CommentOrNewline;
|
use bumpalo::collections::vec::Vec;
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use roc_module::called_via::{BinOp, UnaryOp};
|
||||||
|
use roc_parse::{
|
||||||
|
ast::{
|
||||||
|
AbilityMember, AssignedField, Collection, CommentOrNewline, Def, Expr, Has, HasClause,
|
||||||
|
Module, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader,
|
||||||
|
ValueDef, WhenBranch,
|
||||||
|
},
|
||||||
|
header::{
|
||||||
|
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName,
|
||||||
|
PackageEntry, PackageName, PlatformHeader, PlatformRequires, To, TypedIdent,
|
||||||
|
},
|
||||||
|
ident::UppercaseIdent,
|
||||||
|
};
|
||||||
|
use roc_region::all::{Loc, Region};
|
||||||
|
|
||||||
use crate::Buf;
|
use crate::{Ast, Buf};
|
||||||
|
|
||||||
/// The number of spaces to indent.
|
/// The number of spaces to indent.
|
||||||
pub const INDENT: u16 = 4;
|
pub const INDENT: u16 = 4;
|
||||||
|
@ -149,3 +164,575 @@ fn fmt_docs<'buf>(buf: &mut Buf<'buf>, docs: &str) {
|
||||||
}
|
}
|
||||||
buf.push_str(docs);
|
buf.push_str(docs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
|
||||||
|
///
|
||||||
|
/// Currently this consists of:
|
||||||
|
/// * Removing newlines
|
||||||
|
/// * Removing comments
|
||||||
|
/// * Removing parens in Exprs
|
||||||
|
///
|
||||||
|
/// Long term, we actuall want this transform to preserve comments (so we can assert they're maintained by formatting)
|
||||||
|
/// - but there are currently several bugs where they're _not_ preserved.
|
||||||
|
/// TODO: ensure formatting retains comments
|
||||||
|
pub trait RemoveSpaces<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Ast<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
Ast {
|
||||||
|
module: self.module.remove_spaces(arena),
|
||||||
|
defs: {
|
||||||
|
let mut defs = Vec::with_capacity_in(self.defs.len(), arena);
|
||||||
|
for d in &self.defs {
|
||||||
|
defs.push(d.remove_spaces(arena))
|
||||||
|
}
|
||||||
|
defs
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Module<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match self {
|
||||||
|
Module::Interface { header } => Module::Interface {
|
||||||
|
header: InterfaceHeader {
|
||||||
|
name: header.name.remove_spaces(arena),
|
||||||
|
exposes: header.exposes.remove_spaces(arena),
|
||||||
|
imports: header.imports.remove_spaces(arena),
|
||||||
|
before_header: &[],
|
||||||
|
after_interface_keyword: &[],
|
||||||
|
before_exposes: &[],
|
||||||
|
after_exposes: &[],
|
||||||
|
before_imports: &[],
|
||||||
|
after_imports: &[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Module::App { header } => Module::App {
|
||||||
|
header: AppHeader {
|
||||||
|
name: header.name.remove_spaces(arena),
|
||||||
|
packages: header.packages.remove_spaces(arena),
|
||||||
|
imports: header.imports.remove_spaces(arena),
|
||||||
|
provides: header.provides.remove_spaces(arena),
|
||||||
|
provides_types: header.provides_types.map(|ts| ts.remove_spaces(arena)),
|
||||||
|
to: header.to.remove_spaces(arena),
|
||||||
|
before_header: &[],
|
||||||
|
after_app_keyword: &[],
|
||||||
|
before_packages: &[],
|
||||||
|
after_packages: &[],
|
||||||
|
before_imports: &[],
|
||||||
|
after_imports: &[],
|
||||||
|
before_provides: &[],
|
||||||
|
after_provides: &[],
|
||||||
|
before_to: &[],
|
||||||
|
after_to: &[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Module::Platform { header } => Module::Platform {
|
||||||
|
header: PlatformHeader {
|
||||||
|
name: header.name.remove_spaces(arena),
|
||||||
|
requires: header.requires.remove_spaces(arena),
|
||||||
|
exposes: header.exposes.remove_spaces(arena),
|
||||||
|
packages: header.packages.remove_spaces(arena),
|
||||||
|
imports: header.imports.remove_spaces(arena),
|
||||||
|
provides: header.provides.remove_spaces(arena),
|
||||||
|
before_header: &[],
|
||||||
|
after_platform_keyword: &[],
|
||||||
|
before_requires: &[],
|
||||||
|
after_requires: &[],
|
||||||
|
before_exposes: &[],
|
||||||
|
after_exposes: &[],
|
||||||
|
before_packages: &[],
|
||||||
|
after_packages: &[],
|
||||||
|
before_imports: &[],
|
||||||
|
after_imports: &[],
|
||||||
|
before_provides: &[],
|
||||||
|
after_provides: &[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Module::Hosted { header } => Module::Hosted {
|
||||||
|
header: HostedHeader {
|
||||||
|
name: header.name.remove_spaces(arena),
|
||||||
|
exposes: header.exposes.remove_spaces(arena),
|
||||||
|
imports: header.imports.remove_spaces(arena),
|
||||||
|
generates: header.generates.remove_spaces(arena),
|
||||||
|
generates_with: header.generates_with.remove_spaces(arena),
|
||||||
|
before_header: &[],
|
||||||
|
after_hosted_keyword: &[],
|
||||||
|
before_exposes: &[],
|
||||||
|
after_exposes: &[],
|
||||||
|
before_imports: &[],
|
||||||
|
after_imports: &[],
|
||||||
|
before_generates: &[],
|
||||||
|
after_generates: &[],
|
||||||
|
before_with: &[],
|
||||||
|
after_with: &[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for &'a str {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)),
|
||||||
|
Spaced::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
|
Spaced::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for ExposedName<'a> {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for ModuleName<'a> {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for PackageName<'a> {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for To<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
To::ExistingPackage(a) => To::ExistingPackage(a),
|
||||||
|
To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for TypedIdent<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
TypedIdent {
|
||||||
|
ident: self.ident.remove_spaces(arena),
|
||||||
|
spaces_before_colon: &[],
|
||||||
|
ann: self.ann.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
PlatformRequires {
|
||||||
|
rigids: self.rigids.remove_spaces(arena),
|
||||||
|
signature: self.signature.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for PackageEntry<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
PackageEntry {
|
||||||
|
shorthand: self.shorthand,
|
||||||
|
spaces_after_shorthand: &[],
|
||||||
|
package_name: self.package_name.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)),
|
||||||
|
ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option<T> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
self.as_ref().map(|a| a.remove_spaces(arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc<T> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
let res = self.value.remove_spaces(arena);
|
||||||
|
Loc::at(Region::zero(), res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
(self.0.remove_spaces(arena), self.1.remove_spaces(arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
let mut items = Vec::with_capacity_in(self.items.len(), arena);
|
||||||
|
for item in self.items {
|
||||||
|
items.push(item.remove_spaces(arena));
|
||||||
|
}
|
||||||
|
Collection::with_items(items.into_bump_slice())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
let mut items = Vec::with_capacity_in(self.len(), arena);
|
||||||
|
for item in *self {
|
||||||
|
let res = item.remove_spaces(arena);
|
||||||
|
items.push(res);
|
||||||
|
}
|
||||||
|
items.into_bump_slice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for UnaryOp {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for BinOp {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
arena.alloc((*self).remove_spaces(arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
use TypeDef::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader { name, vars },
|
||||||
|
ann,
|
||||||
|
} => Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: name.remove_spaces(arena),
|
||||||
|
vars: vars.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
ann: ann.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
Opaque {
|
||||||
|
header: TypeHeader { name, vars },
|
||||||
|
typ,
|
||||||
|
} => Opaque {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: name.remove_spaces(arena),
|
||||||
|
vars: vars.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
typ: typ.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
Ability {
|
||||||
|
header: TypeHeader { name, vars },
|
||||||
|
loc_has,
|
||||||
|
members,
|
||||||
|
} => Ability {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: name.remove_spaces(arena),
|
||||||
|
vars: vars.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
loc_has: loc_has.remove_spaces(arena),
|
||||||
|
members: members.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
use ValueDef::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)),
|
||||||
|
Body(a, b) => Body(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
AnnotatedBody {
|
||||||
|
ann_pattern,
|
||||||
|
ann_type,
|
||||||
|
comment: _,
|
||||||
|
body_pattern,
|
||||||
|
body_expr,
|
||||||
|
} => AnnotatedBody {
|
||||||
|
ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)),
|
||||||
|
ann_type: arena.alloc(ann_type.remove_spaces(arena)),
|
||||||
|
comment: None,
|
||||||
|
body_pattern: arena.alloc(body_pattern.remove_spaces(arena)),
|
||||||
|
body_expr: arena.alloc(body_expr.remove_spaces(arena)),
|
||||||
|
},
|
||||||
|
Expect(a) => Expect(arena.alloc(a.remove_spaces(arena))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Def<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
Def::Type(def) => Def::Type(def.remove_spaces(arena)),
|
||||||
|
Def::Value(def) => Def::Value(def.remove_spaces(arena)),
|
||||||
|
Def::NotYetImplemented(a) => Def::NotYetImplemented(a),
|
||||||
|
Def::SpaceBefore(a, _) | Def::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Has<'a> {
|
||||||
|
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
|
||||||
|
Has::Has
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for AbilityMember<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
AbilityMember {
|
||||||
|
name: self.name.remove_spaces(arena),
|
||||||
|
typ: self.typ.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for WhenBranch<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
WhenBranch {
|
||||||
|
patterns: self.patterns.remove_spaces(arena),
|
||||||
|
value: self.value.remove_spaces(arena),
|
||||||
|
guard: self.guard.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue(
|
||||||
|
a.remove_spaces(arena),
|
||||||
|
arena.alloc([]),
|
||||||
|
arena.alloc(c.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue(
|
||||||
|
a.remove_spaces(arena),
|
||||||
|
arena.alloc([]),
|
||||||
|
arena.alloc(c.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)),
|
||||||
|
AssignedField::Malformed(a) => AssignedField::Malformed(a),
|
||||||
|
AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
|
AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for StrLiteral<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
|
||||||
|
StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)),
|
||||||
|
StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for StrSegment<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
StrSegment::Plaintext(t) => StrSegment::Plaintext(t),
|
||||||
|
StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)),
|
||||||
|
StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c),
|
||||||
|
StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
Expr::Float(a) => Expr::Float(a),
|
||||||
|
Expr::Num(a) => Expr::Num(a),
|
||||||
|
Expr::NonBase10Int {
|
||||||
|
string,
|
||||||
|
base,
|
||||||
|
is_negative,
|
||||||
|
} => Expr::NonBase10Int {
|
||||||
|
string,
|
||||||
|
base,
|
||||||
|
is_negative,
|
||||||
|
},
|
||||||
|
Expr::Str(a) => Expr::Str(a.remove_spaces(arena)),
|
||||||
|
Expr::Access(a, b) => Expr::Access(arena.alloc(a.remove_spaces(arena)), b),
|
||||||
|
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
|
||||||
|
Expr::List(a) => Expr::List(a.remove_spaces(arena)),
|
||||||
|
Expr::RecordUpdate { update, fields } => Expr::RecordUpdate {
|
||||||
|
update: arena.alloc(update.remove_spaces(arena)),
|
||||||
|
fields: fields.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
||||||
|
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||||
|
Expr::Underscore(a) => Expr::Underscore(a),
|
||||||
|
Expr::Tag(a) => Expr::Tag(a),
|
||||||
|
Expr::OpaqueRef(a) => Expr::OpaqueRef(a),
|
||||||
|
Expr::Closure(a, b) => Expr::Closure(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
Expr::Defs(a, b) => {
|
||||||
|
Expr::Defs(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
|
||||||
|
}
|
||||||
|
Expr::Backpassing(a, b, c) => Expr::Backpassing(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
|
arena.alloc(c.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
Expr::Expect(a, b) => Expr::Expect(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
Expr::Apply(a, b, c) => Expr::Apply(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
b.remove_spaces(arena),
|
||||||
|
c,
|
||||||
|
),
|
||||||
|
Expr::BinOps(a, b) => {
|
||||||
|
Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena)))
|
||||||
|
}
|
||||||
|
Expr::UnaryOp(a, b) => {
|
||||||
|
Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
||||||
|
}
|
||||||
|
Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))),
|
||||||
|
Expr::When(a, b) => {
|
||||||
|
Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena))
|
||||||
|
}
|
||||||
|
Expr::ParensAround(a) => {
|
||||||
|
// The formatter can remove redundant parentheses, so also remove these when normalizing for comparison.
|
||||||
|
a.remove_spaces(arena)
|
||||||
|
}
|
||||||
|
Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, b),
|
||||||
|
Expr::MalformedClosure => Expr::MalformedClosure,
|
||||||
|
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
||||||
|
Expr::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
|
Expr::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
Expr::SingleQuote(a) => Expr::Num(a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
Pattern::Identifier(a) => Pattern::Identifier(a),
|
||||||
|
Pattern::Tag(a) => Pattern::Tag(a),
|
||||||
|
Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a),
|
||||||
|
Pattern::Apply(a, b) => Pattern::Apply(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)),
|
||||||
|
Pattern::RequiredField(a, b) => {
|
||||||
|
Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena)))
|
||||||
|
}
|
||||||
|
Pattern::OptionalField(a, b) => {
|
||||||
|
Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena)))
|
||||||
|
}
|
||||||
|
Pattern::NumLiteral(a) => Pattern::NumLiteral(a),
|
||||||
|
Pattern::NonBase10Literal {
|
||||||
|
string,
|
||||||
|
base,
|
||||||
|
is_negative,
|
||||||
|
} => Pattern::NonBase10Literal {
|
||||||
|
string,
|
||||||
|
base,
|
||||||
|
is_negative,
|
||||||
|
},
|
||||||
|
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
|
||||||
|
Pattern::StrLiteral(a) => Pattern::StrLiteral(a),
|
||||||
|
Pattern::Underscore(a) => Pattern::Underscore(a),
|
||||||
|
Pattern::Malformed(a) => Pattern::Malformed(a),
|
||||||
|
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, b),
|
||||||
|
Pattern::QualifiedIdentifier { module_name, ident } => {
|
||||||
|
Pattern::QualifiedIdentifier { module_name, ident }
|
||||||
|
}
|
||||||
|
Pattern::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
|
Pattern::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
Pattern::SingleQuote(a) => Pattern::NumLiteral(a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
TypeAnnotation::Function(a, b) => TypeAnnotation::Function(
|
||||||
|
arena.alloc(a.remove_spaces(arena)),
|
||||||
|
arena.alloc(b.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)),
|
||||||
|
TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a),
|
||||||
|
TypeAnnotation::As(a, _, c) => {
|
||||||
|
TypeAnnotation::As(arena.alloc(a.remove_spaces(arena)), &[], c)
|
||||||
|
}
|
||||||
|
TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record {
|
||||||
|
fields: fields.remove_spaces(arena),
|
||||||
|
ext: ext.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion {
|
||||||
|
ext: ext.remove_spaces(arena),
|
||||||
|
tags: tags.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
TypeAnnotation::Inferred => TypeAnnotation::Inferred,
|
||||||
|
TypeAnnotation::Wildcard => TypeAnnotation::Wildcard,
|
||||||
|
TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where(
|
||||||
|
arena.alloc(annot.remove_spaces(arena)),
|
||||||
|
arena.alloc(has_clauses.remove_spaces(arena)),
|
||||||
|
),
|
||||||
|
TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
|
TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for HasClause<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
HasClause {
|
||||||
|
var: self.var.remove_spaces(arena),
|
||||||
|
ability: self.ability.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Tag<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match *self {
|
||||||
|
Tag::Apply { name, args } => Tag::Apply {
|
||||||
|
name: name.remove_spaces(arena),
|
||||||
|
args: args.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
Tag::Malformed(a) => Tag::Malformed(a),
|
||||||
|
Tag::SpaceBefore(a, _) => a.remove_spaces(arena),
|
||||||
|
Tag::SpaceAfter(a, _) => a.remove_spaces(arena),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,60 +10,153 @@ mod test_fmt {
|
||||||
use roc_fmt::def::fmt_def;
|
use roc_fmt::def::fmt_def;
|
||||||
use roc_fmt::module::fmt_module;
|
use roc_fmt::module::fmt_module;
|
||||||
use roc_fmt::Buf;
|
use roc_fmt::Buf;
|
||||||
|
use roc_parse::ast::Module;
|
||||||
use roc_parse::module::{self, module_defs};
|
use roc_parse::module::{self, module_defs};
|
||||||
use roc_parse::parser::Parser;
|
use roc_parse::parser::Parser;
|
||||||
use roc_parse::state::State;
|
use roc_parse::state::State;
|
||||||
use roc_test_utils::assert_multiline_str_eq;
|
use roc_test_utils::assert_multiline_str_eq;
|
||||||
|
|
||||||
// Not intended to be used directly in tests; please use expr_formats_to or expr_formats_same
|
// Not intended to be used directly in tests; please use expr_formats_to or expr_formats_same
|
||||||
fn expect_format_expr_helper(input: &str, expected: &str) {
|
fn expr_formats_to(input: &str, expected: &str) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
match roc_parse::test_helpers::parse_expr_with(&arena, input.trim()) {
|
let input = input.trim();
|
||||||
|
let expected = expected.trim();
|
||||||
|
|
||||||
|
match roc_parse::test_helpers::parse_expr_with(&arena, input) {
|
||||||
Ok(actual) => {
|
Ok(actual) => {
|
||||||
|
use roc_fmt::spaces::RemoveSpaces;
|
||||||
|
|
||||||
let mut buf = Buf::new_in(&arena);
|
let mut buf = Buf::new_in(&arena);
|
||||||
|
|
||||||
actual.format_with_options(&mut buf, Parens::NotNeeded, Newlines::Yes, 0);
|
actual.format_with_options(&mut buf, Parens::NotNeeded, Newlines::Yes, 0);
|
||||||
|
|
||||||
assert_multiline_str_eq!(expected, buf.as_str());
|
let output = buf.as_str();
|
||||||
|
|
||||||
|
assert_multiline_str_eq!(expected, output);
|
||||||
|
|
||||||
|
let reparsed_ast = roc_parse::test_helpers::parse_expr_with(&arena, output).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"After formatting, the source code no longer parsed!\n\nParse error was: {:?}\n\nThe code that failed to parse:\n\n{}\n\n",
|
||||||
|
err, output
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast_normalized = actual.remove_spaces(&arena);
|
||||||
|
let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena);
|
||||||
|
|
||||||
|
// HACK!
|
||||||
|
// We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast,
|
||||||
|
// the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same.
|
||||||
|
// I don't have the patience to debug this right now, so let's leave it for another day...
|
||||||
|
// TODO: fix PartialEq impl on ast types
|
||||||
|
if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) {
|
||||||
|
panic!(
|
||||||
|
"Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\
|
||||||
|
* * * Source code before formatting:\n{}\n\n\
|
||||||
|
* * * Source code after formatting:\n{}\n\n",
|
||||||
|
input,
|
||||||
|
output
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now verify that the resultant formatting is _stable_ - i.e. that it doesn't change again if re-formatted
|
||||||
|
let mut reformatted_buf = Buf::new_in(&arena);
|
||||||
|
reparsed_ast.format_with_options(&mut reformatted_buf, Parens::NotNeeded, Newlines::Yes, 0);
|
||||||
|
|
||||||
|
if output != reformatted_buf.as_str() {
|
||||||
|
eprintln!("Formatting bug; formatting is not stable. Reformatting the formatted code changed it again, as follows:\n\n");
|
||||||
|
|
||||||
|
assert_multiline_str_eq!(output, reformatted_buf.as_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_formats_to(input: &str, expected: &str) {
|
|
||||||
let input = input.trim_end();
|
|
||||||
let expected = expected.trim_end();
|
|
||||||
|
|
||||||
// First check that input formats to the expected version
|
|
||||||
expect_format_expr_helper(input, expected);
|
|
||||||
|
|
||||||
// Parse the expected result format it, asserting that it doesn't change
|
|
||||||
// It's important that formatting be stable / idempotent
|
|
||||||
expect_format_expr_helper(expected, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expr_formats_same(input: &str) {
|
fn expr_formats_same(input: &str) {
|
||||||
expr_formats_to(input, input);
|
expr_formats_to(input, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_module_and_defs<'a>(
|
||||||
|
arena: &Bump,
|
||||||
|
src: &str,
|
||||||
|
module: &Module<'a>,
|
||||||
|
state: State<'a>,
|
||||||
|
buf: &mut Buf<'_>,
|
||||||
|
) {
|
||||||
|
fmt_module(buf, module);
|
||||||
|
|
||||||
|
match module_defs().parse(&arena, state) {
|
||||||
|
Ok((_, loc_defs, _)) => {
|
||||||
|
for loc_def in loc_defs {
|
||||||
|
fmt_def(buf, arena.alloc(loc_def.value), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => panic!("Unexpected parse failure when parsing this for defs formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Not intended to be used directly in tests; please use module_formats_to or module_formats_same
|
// Not intended to be used directly in tests; please use module_formats_to or module_formats_same
|
||||||
fn expect_format_module_helper(src: &str, expected: &str) {
|
fn expect_format_module_helper(src: &str, expected: &str) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
let src = src.trim();
|
||||||
|
let expected = expected.trim();
|
||||||
|
|
||||||
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
match module::parse_header(&arena, State::new(src.as_bytes())) {
|
||||||
Ok((actual, state)) => {
|
Ok((actual, state)) => {
|
||||||
|
use roc_fmt::spaces::RemoveSpaces;
|
||||||
|
|
||||||
let mut buf = Buf::new_in(&arena);
|
let mut buf = Buf::new_in(&arena);
|
||||||
|
|
||||||
fmt_module(&mut buf, &actual);
|
fmt_module_and_defs(&arena, src, &actual, state, &mut buf);
|
||||||
|
|
||||||
match module_defs().parse(&arena, state) {
|
let output = buf.as_str().trim();
|
||||||
Ok((_, loc_defs, _)) => {
|
|
||||||
for loc_def in loc_defs {
|
let (reparsed_ast, state) = module::parse_header(&arena, State::new(output.as_bytes())).unwrap_or_else(|err| {
|
||||||
fmt_def(&mut buf, arena.alloc(loc_def.value), 0);
|
panic!(
|
||||||
}
|
"After formatting, the source code no longer parsed!\n\nParse error was: {:?}\n\nThe code that failed to parse:\n\n{}\n\n",
|
||||||
}
|
err, output
|
||||||
Err(error) => panic!("Unexpected parse failure when parsing this for defs formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error)
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast_normalized = actual.remove_spaces(&arena);
|
||||||
|
let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena);
|
||||||
|
|
||||||
|
// HACK!
|
||||||
|
// We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast,
|
||||||
|
// the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same.
|
||||||
|
// I don't have the patience to debug this right now, so let's leave it for another day...
|
||||||
|
// TODO: fix PartialEq impl on ast types
|
||||||
|
if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) {
|
||||||
|
panic!(
|
||||||
|
"Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\
|
||||||
|
* * * Source code before formatting:\n{}\n\n\
|
||||||
|
* * * Source code after formatting:\n{}\n\n",
|
||||||
|
src,
|
||||||
|
output
|
||||||
|
);
|
||||||
}
|
}
|
||||||
assert_multiline_str_eq!(expected, buf.as_str())
|
|
||||||
|
// Now verify that the resultant formatting is _stable_ - i.e. that it doesn't change again if re-formatted
|
||||||
|
let mut reformatted_buf = Buf::new_in(&arena);
|
||||||
|
|
||||||
|
fmt_module_and_defs(&arena, output, &reparsed_ast, state, &mut reformatted_buf);
|
||||||
|
|
||||||
|
let reformatted = reformatted_buf.as_str().trim();
|
||||||
|
|
||||||
|
if output != reformatted {
|
||||||
|
eprintln!("Formatting bug; formatting is not stable. Reformatting the formatted code changed it again, as follows:\n\n");
|
||||||
|
|
||||||
|
assert_multiline_str_eq!(output, reformatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If everything was idempotent re-parsing worked, finally assert
|
||||||
|
// that the formatted code was what we expected it to be.
|
||||||
|
//
|
||||||
|
// Do this last because if there were any serious problems with the
|
||||||
|
// formatter (e.g. it wasn't idempotent), we want to know about
|
||||||
|
// those more than we want to know that the expectation failed!
|
||||||
|
assert_multiline_str_eq!(expected, output);
|
||||||
}
|
}
|
||||||
Err(error) => panic!("Unexpected parse failure when parsing this for module header formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error)
|
Err(error) => panic!("Unexpected parse failure when parsing this for module header formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", src, error)
|
||||||
};
|
};
|
||||||
|
@ -3939,6 +4032,39 @@ mod test_fmt {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn format_tui_package_config() {
|
||||||
|
// At one point this failed to reformat.
|
||||||
|
module_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
platform "tui"
|
||||||
|
requires { Model } { main : { init : ({} -> Model), update : (Model, Str -> Model), view : (Model -> Str) } }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [ mainForHost ]
|
||||||
|
|
||||||
|
mainForHost : { init : ({} -> Model) as Init, update : (Model, Str -> Model) as Update, view : (Model -> Str) as View }
|
||||||
|
mainForHost = main
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
platform "tui"
|
||||||
|
requires { Model } { main : { init : {} -> Model, update : Model, Str -> Model, view : Model -> Str } }
|
||||||
|
exposes []
|
||||||
|
packages {}
|
||||||
|
imports []
|
||||||
|
provides [ mainForHost ]
|
||||||
|
|
||||||
|
mainForHost : { init : ({} -> Model) as Init, update : (Model, Str -> Model) as Update, view : (Model -> Str) as View }
|
||||||
|
mainForHost = main
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn single_line_hosted() {
|
fn single_line_hosted() {
|
||||||
module_formats_same(indoc!(
|
module_formats_same(indoc!(
|
||||||
|
|
|
@ -554,7 +554,7 @@ trait Backend<'a> {
|
||||||
}
|
}
|
||||||
LowLevel::NumRound => self.build_fn_call(
|
LowLevel::NumRound => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::NUM_ROUND[FloatWidth::F64].to_string(),
|
bitcode::NUM_ROUND_F64[IntWidth::I64].to_string(),
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
|
|
@ -602,6 +602,7 @@ static LLVM_SIN: IntrinsicName = float_intrinsic!("llvm.sin");
|
||||||
static LLVM_COS: IntrinsicName = float_intrinsic!("llvm.cos");
|
static LLVM_COS: IntrinsicName = float_intrinsic!("llvm.cos");
|
||||||
static LLVM_CEILING: IntrinsicName = float_intrinsic!("llvm.ceil");
|
static LLVM_CEILING: IntrinsicName = float_intrinsic!("llvm.ceil");
|
||||||
static LLVM_FLOOR: IntrinsicName = float_intrinsic!("llvm.floor");
|
static LLVM_FLOOR: IntrinsicName = float_intrinsic!("llvm.floor");
|
||||||
|
static LLVM_ROUND: IntrinsicName = float_intrinsic!("llvm.round");
|
||||||
|
|
||||||
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
||||||
static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
|
static LLVM_MEMSET_I32: &str = "llvm.memset.p0i8.i32";
|
||||||
|
@ -7403,20 +7404,67 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumCeiling => env.builder.build_cast(
|
NumCeiling => {
|
||||||
InstructionOpcode::FPToSI,
|
let (return_signed, return_type) = match layout {
|
||||||
env.call_intrinsic(&LLVM_CEILING[float_width], &[arg.into()]),
|
Layout::Builtin(Builtin::Int(int_width)) => (
|
||||||
env.context.i64_type(),
|
int_width.is_signed(),
|
||||||
"num_ceiling",
|
convert::int_type_from_int_width(env, *int_width),
|
||||||
),
|
),
|
||||||
NumFloor => env.builder.build_cast(
|
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
||||||
InstructionOpcode::FPToSI,
|
};
|
||||||
env.call_intrinsic(&LLVM_FLOOR[float_width], &[arg.into()]),
|
let opcode = if return_signed {
|
||||||
env.context.i64_type(),
|
InstructionOpcode::FPToSI
|
||||||
"num_floor",
|
} else {
|
||||||
),
|
InstructionOpcode::FPToUI
|
||||||
|
};
|
||||||
|
env.builder.build_cast(
|
||||||
|
opcode,
|
||||||
|
env.call_intrinsic(&LLVM_CEILING[float_width], &[arg.into()]),
|
||||||
|
return_type,
|
||||||
|
"num_ceiling",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
NumFloor => {
|
||||||
|
let (return_signed, return_type) = match layout {
|
||||||
|
Layout::Builtin(Builtin::Int(int_width)) => (
|
||||||
|
int_width.is_signed(),
|
||||||
|
convert::int_type_from_int_width(env, *int_width),
|
||||||
|
),
|
||||||
|
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
||||||
|
};
|
||||||
|
let opcode = if return_signed {
|
||||||
|
InstructionOpcode::FPToSI
|
||||||
|
} else {
|
||||||
|
InstructionOpcode::FPToUI
|
||||||
|
};
|
||||||
|
env.builder.build_cast(
|
||||||
|
opcode,
|
||||||
|
env.call_intrinsic(&LLVM_FLOOR[float_width], &[arg.into()]),
|
||||||
|
return_type,
|
||||||
|
"num_floor",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
NumRound => {
|
||||||
|
let (return_signed, return_type) = match layout {
|
||||||
|
Layout::Builtin(Builtin::Int(int_width)) => (
|
||||||
|
int_width.is_signed(),
|
||||||
|
convert::int_type_from_int_width(env, *int_width),
|
||||||
|
),
|
||||||
|
_ => internal_error!("Ceiling return layout is not int: {:?}", layout),
|
||||||
|
};
|
||||||
|
let opcode = if return_signed {
|
||||||
|
InstructionOpcode::FPToSI
|
||||||
|
} else {
|
||||||
|
InstructionOpcode::FPToUI
|
||||||
|
};
|
||||||
|
env.builder.build_cast(
|
||||||
|
opcode,
|
||||||
|
env.call_intrinsic(&LLVM_ROUND[float_width], &[arg.into()]),
|
||||||
|
return_type,
|
||||||
|
"num_round",
|
||||||
|
)
|
||||||
|
}
|
||||||
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
|
NumIsFinite => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_IS_FINITE[float_width]),
|
||||||
NumRound => call_bitcode_fn(env, &[arg.into()], &bitcode::NUM_ROUND[float_width]),
|
|
||||||
|
|
||||||
// trigonometry
|
// trigonometry
|
||||||
NumSin => env.call_intrinsic(&LLVM_SIN[float_width], &[arg.into()]),
|
NumSin => env.call_intrinsic(&LLVM_SIN[float_width], &[arg.into()]),
|
||||||
|
|
|
@ -561,18 +561,6 @@ impl<'a> LowLevelCall<'a> {
|
||||||
NumCos => todo!("{:?}", self.lowlevel),
|
NumCos => todo!("{:?}", self.lowlevel),
|
||||||
NumSqrtUnchecked => todo!("{:?}", self.lowlevel),
|
NumSqrtUnchecked => todo!("{:?}", self.lowlevel),
|
||||||
NumLogUnchecked => todo!("{:?}", self.lowlevel),
|
NumLogUnchecked => todo!("{:?}", self.lowlevel),
|
||||||
NumRound => {
|
|
||||||
self.load_args(backend);
|
|
||||||
match CodeGenNumType::for_symbol(backend, self.arguments[0]) {
|
|
||||||
F32 => {
|
|
||||||
self.load_args_and_call_zig(backend, &bitcode::NUM_ROUND[FloatWidth::F32])
|
|
||||||
}
|
|
||||||
F64 => {
|
|
||||||
self.load_args_and_call_zig(backend, &bitcode::NUM_ROUND[FloatWidth::F64])
|
|
||||||
}
|
|
||||||
_ => todo!("{:?} for {:?}", self.lowlevel, self.ret_layout),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NumToFloat => {
|
NumToFloat => {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
let ret_type = CodeGenNumType::from(self.ret_layout);
|
let ret_type = CodeGenNumType::from(self.ret_layout);
|
||||||
|
@ -592,35 +580,54 @@ impl<'a> LowLevelCall<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumPow => todo!("{:?}", self.lowlevel),
|
NumPow => todo!("{:?}", self.lowlevel),
|
||||||
NumCeiling => {
|
NumRound => {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
match CodeGenNumType::from(self.ret_layout) {
|
let arg_type = CodeGenNumType::for_symbol(backend, self.arguments[0]);
|
||||||
I32 => {
|
let ret_type = CodeGenNumType::from(self.ret_layout);
|
||||||
|
|
||||||
|
let width = match ret_type {
|
||||||
|
CodeGenNumType::I32 => IntWidth::I32,
|
||||||
|
CodeGenNumType::I64 => IntWidth::I64,
|
||||||
|
CodeGenNumType::I128 => todo!("{:?} for I128", self.lowlevel),
|
||||||
|
_ => internal_error!("Invalid return type for round: {:?}", ret_type),
|
||||||
|
};
|
||||||
|
|
||||||
|
match arg_type {
|
||||||
|
F32 => self.load_args_and_call_zig(backend, &bitcode::NUM_ROUND_F32[width]),
|
||||||
|
F64 => self.load_args_and_call_zig(backend, &bitcode::NUM_ROUND_F64[width]),
|
||||||
|
_ => internal_error!("Invalid argument type for round: {:?}", arg_type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumCeiling | NumFloor => {
|
||||||
|
self.load_args(backend);
|
||||||
|
let arg_type = CodeGenNumType::for_symbol(backend, self.arguments[0]);
|
||||||
|
let ret_type = CodeGenNumType::from(self.ret_layout);
|
||||||
|
match (arg_type, self.lowlevel) {
|
||||||
|
(F32, NumCeiling) => {
|
||||||
backend.code_builder.f32_ceil();
|
backend.code_builder.f32_ceil();
|
||||||
backend.code_builder.i32_trunc_s_f32()
|
|
||||||
}
|
}
|
||||||
I64 => {
|
(F64, NumCeiling) => {
|
||||||
backend.code_builder.f64_ceil();
|
backend.code_builder.f64_ceil();
|
||||||
backend.code_builder.i64_trunc_s_f64()
|
|
||||||
}
|
}
|
||||||
|
(F32, NumFloor) => {
|
||||||
|
backend.code_builder.f32_floor();
|
||||||
|
}
|
||||||
|
(F64, NumFloor) => {
|
||||||
|
backend.code_builder.f64_floor();
|
||||||
|
}
|
||||||
|
_ => internal_error!("Invalid argument type for ceiling: {:?}", arg_type),
|
||||||
|
}
|
||||||
|
match (ret_type, arg_type) {
|
||||||
|
// TODO: unsigned truncation
|
||||||
|
(I32, F32) => backend.code_builder.i32_trunc_s_f32(),
|
||||||
|
(I32, F64) => backend.code_builder.i32_trunc_s_f64(),
|
||||||
|
(I64, F32) => backend.code_builder.i64_trunc_s_f32(),
|
||||||
|
(I64, F64) => backend.code_builder.i64_trunc_s_f64(),
|
||||||
|
(I128, _) => todo!("{:?} for I128", self.lowlevel),
|
||||||
_ => panic_ret_type(),
|
_ => panic_ret_type(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumPowInt => todo!("{:?}", self.lowlevel),
|
NumPowInt => todo!("{:?}", self.lowlevel),
|
||||||
NumFloor => {
|
|
||||||
self.load_args(backend);
|
|
||||||
match CodeGenNumType::from(self.ret_layout) {
|
|
||||||
I32 => {
|
|
||||||
backend.code_builder.f32_floor();
|
|
||||||
backend.code_builder.i32_trunc_s_f32()
|
|
||||||
}
|
|
||||||
I64 => {
|
|
||||||
backend.code_builder.f64_floor();
|
|
||||||
backend.code_builder.i64_trunc_s_f64()
|
|
||||||
}
|
|
||||||
_ => panic_ret_type(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NumIsFinite => num_is_finite(backend, self.arguments[0]),
|
NumIsFinite => num_is_finite(backend, self.arguments[0]),
|
||||||
|
|
||||||
NumAtan => match self.ret_layout {
|
NumAtan => match self.ret_layout {
|
||||||
|
|
|
@ -4,6 +4,3 @@ version = "0.1.0"
|
||||||
authors = ["The Roc Contributors"]
|
authors = ["The Roc Contributors"]
|
||||||
license = "UPL-1.0"
|
license = "UPL-1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
arrayvec = "0.7.2"
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ fn write_subs_for_module(module_id: ModuleId, filename: &str) {
|
||||||
Default::default(),
|
Default::default(),
|
||||||
target_info,
|
target_info,
|
||||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||||
Threading::Multi,
|
Threading::AllAvailable,
|
||||||
);
|
);
|
||||||
|
|
||||||
let module = res_module.unwrap();
|
let module = res_module.unwrap();
|
||||||
|
|
|
@ -22,15 +22,12 @@ roc_mono = { path = "../mono" }
|
||||||
roc_target = { path = "../roc_target" }
|
roc_target = { path = "../roc_target" }
|
||||||
roc_reporting = { path = "../../reporting" }
|
roc_reporting = { path = "../../reporting" }
|
||||||
roc_debug_flags = { path = "../debug_flags" }
|
roc_debug_flags = { path = "../debug_flags" }
|
||||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
|
||||||
ven_pretty = { path = "../../vendor/pretty" }
|
ven_pretty = { path = "../../vendor/pretty" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
crossbeam = "0.8.1"
|
crossbeam = "0.8.1"
|
||||||
num_cpus = "1.13.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.2.0"
|
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
|
|
|
@ -747,6 +747,7 @@ impl<'a> State<'a> {
|
||||||
ident_ids_by_module: SharedIdentIdsByModule,
|
ident_ids_by_module: SharedIdentIdsByModule,
|
||||||
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
||||||
render: RenderTarget,
|
render: RenderTarget,
|
||||||
|
number_of_workers: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let arc_shorthands = Arc::new(Mutex::new(MutMap::default()));
|
let arc_shorthands = Arc::new(Mutex::new(MutMap::default()));
|
||||||
|
|
||||||
|
@ -770,7 +771,7 @@ impl<'a> State<'a> {
|
||||||
declarations_by_id: MutMap::default(),
|
declarations_by_id: MutMap::default(),
|
||||||
exposed_symbols_by_module: MutMap::default(),
|
exposed_symbols_by_module: MutMap::default(),
|
||||||
timings: MutMap::default(),
|
timings: MutMap::default(),
|
||||||
layout_caches: std::vec::Vec::with_capacity(num_cpus::get()),
|
layout_caches: std::vec::Vec::with_capacity(number_of_workers),
|
||||||
cached_subs: Arc::new(Mutex::new(cached_subs)),
|
cached_subs: Arc::new(Mutex::new(cached_subs)),
|
||||||
render,
|
render,
|
||||||
}
|
}
|
||||||
|
@ -1099,7 +1100,8 @@ pub enum LoadResult<'a> {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Threading {
|
pub enum Threading {
|
||||||
Single,
|
Single,
|
||||||
Multi,
|
AllAvailable,
|
||||||
|
AtMost(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The loading process works like this, starting from the given filename (e.g. "main.roc"):
|
/// The loading process works like this, starting from the given filename (e.g. "main.roc"):
|
||||||
|
@ -1157,10 +1159,32 @@ pub fn load<'a>(
|
||||||
render: RenderTarget,
|
render: RenderTarget,
|
||||||
threading: Threading,
|
threading: Threading,
|
||||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||||
// When compiling to wasm, we cannot spawn extra threads
|
enum Threads {
|
||||||
// so we have a single-threaded implementation
|
Single,
|
||||||
if threading == Threading::Single || cfg!(target_family = "wasm") {
|
Many(usize),
|
||||||
load_single_threaded(
|
}
|
||||||
|
|
||||||
|
let threads = {
|
||||||
|
if cfg!(target_family = "wasm") {
|
||||||
|
// When compiling to wasm, we cannot spawn extra threads
|
||||||
|
// so we have a single-threaded implementation
|
||||||
|
Threads::Single
|
||||||
|
} else {
|
||||||
|
match std::thread::available_parallelism().map(|v| v.get()) {
|
||||||
|
Err(_) => Threads::Single,
|
||||||
|
Ok(0) => unreachable!("NonZeroUsize"),
|
||||||
|
Ok(1) => Threads::Single,
|
||||||
|
Ok(reported) => match threading {
|
||||||
|
Threading::Single => Threads::Single,
|
||||||
|
Threading::AllAvailable => Threads::Many(reported),
|
||||||
|
Threading::AtMost(at_most) => Threads::Many(Ord::min(reported, at_most)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match threads {
|
||||||
|
Threads::Single => load_single_threaded(
|
||||||
arena,
|
arena,
|
||||||
load_start,
|
load_start,
|
||||||
src_dir,
|
src_dir,
|
||||||
|
@ -1169,9 +1193,8 @@ pub fn load<'a>(
|
||||||
target_info,
|
target_info,
|
||||||
cached_subs,
|
cached_subs,
|
||||||
render,
|
render,
|
||||||
)
|
),
|
||||||
} else {
|
Threads::Many(threads) => load_multi_threaded(
|
||||||
load_multi_threaded(
|
|
||||||
arena,
|
arena,
|
||||||
load_start,
|
load_start,
|
||||||
src_dir,
|
src_dir,
|
||||||
|
@ -1180,7 +1203,8 @@ pub fn load<'a>(
|
||||||
target_info,
|
target_info,
|
||||||
cached_subs,
|
cached_subs,
|
||||||
render,
|
render,
|
||||||
)
|
threads,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1210,6 +1234,7 @@ pub fn load_single_threaded<'a>(
|
||||||
.send(root_msg)
|
.send(root_msg)
|
||||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||||
|
|
||||||
|
let number_of_workers = 1;
|
||||||
let mut state = State::new(
|
let mut state = State::new(
|
||||||
root_id,
|
root_id,
|
||||||
target_info,
|
target_info,
|
||||||
|
@ -1219,6 +1244,7 @@ pub fn load_single_threaded<'a>(
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
cached_subs,
|
cached_subs,
|
||||||
render,
|
render,
|
||||||
|
number_of_workers,
|
||||||
);
|
);
|
||||||
|
|
||||||
// We'll add tasks to this, and then worker threads will take tasks from it.
|
// We'll add tasks to this, and then worker threads will take tasks from it.
|
||||||
|
@ -1390,6 +1416,7 @@ fn load_multi_threaded<'a>(
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
cached_subs: MutMap<ModuleId, (Subs, Vec<(Symbol, Variable)>)>,
|
||||||
render: RenderTarget,
|
render: RenderTarget,
|
||||||
|
available_threads: usize,
|
||||||
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
) -> Result<LoadResult<'a>, LoadingProblem<'a>> {
|
||||||
let LoadStart {
|
let LoadStart {
|
||||||
arc_modules,
|
arc_modules,
|
||||||
|
@ -1399,6 +1426,28 @@ fn load_multi_threaded<'a>(
|
||||||
..
|
..
|
||||||
} = load_start;
|
} = load_start;
|
||||||
|
|
||||||
|
let (msg_tx, msg_rx) = bounded(1024);
|
||||||
|
msg_tx
|
||||||
|
.send(root_msg)
|
||||||
|
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||||
|
|
||||||
|
// Reserve one CPU for the main thread, and let all the others be eligible
|
||||||
|
// to spawn workers.
|
||||||
|
let available_workers = available_threads - 1;
|
||||||
|
|
||||||
|
let num_workers = match env::var("ROC_NUM_WORKERS") {
|
||||||
|
Ok(env_str) => env_str
|
||||||
|
.parse::<usize>()
|
||||||
|
.unwrap_or(available_workers)
|
||||||
|
.min(available_workers),
|
||||||
|
Err(_) => available_workers,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
num_workers >= 1,
|
||||||
|
"`load_multi_threaded` needs at least one worker"
|
||||||
|
);
|
||||||
|
|
||||||
let mut state = State::new(
|
let mut state = State::new(
|
||||||
root_id,
|
root_id,
|
||||||
target_info,
|
target_info,
|
||||||
|
@ -1408,28 +1457,9 @@ fn load_multi_threaded<'a>(
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
cached_subs,
|
cached_subs,
|
||||||
render,
|
render,
|
||||||
|
num_workers,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (msg_tx, msg_rx) = bounded(1024);
|
|
||||||
msg_tx
|
|
||||||
.send(root_msg)
|
|
||||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
|
||||||
|
|
||||||
// Reserve one CPU for the main thread, and let all the others be eligible
|
|
||||||
// to spawn workers. We use .max(2) to enforce that we always
|
|
||||||
// end up with at least 1 worker - since (.max(2) - 1) will
|
|
||||||
// always return a number that's at least 1. Using
|
|
||||||
// .max(2) on the initial number of CPUs instead of
|
|
||||||
// doing .max(1) on the entire expression guards against
|
|
||||||
// num_cpus returning 0, while also avoiding wrapping
|
|
||||||
// unsigned subtraction overflow.
|
|
||||||
let default_num_workers = num_cpus::get().max(2) - 1;
|
|
||||||
|
|
||||||
let num_workers = match env::var("ROC_NUM_WORKERS") {
|
|
||||||
Ok(env_str) => env_str.parse::<usize>().unwrap_or(default_num_workers),
|
|
||||||
Err(_) => default_num_workers,
|
|
||||||
};
|
|
||||||
|
|
||||||
// an arena for every worker, stored in an arena-allocated bumpalo vec to make the lifetimes work
|
// an arena for every worker, stored in an arena-allocated bumpalo vec to make the lifetimes work
|
||||||
let arenas = std::iter::repeat_with(Bump::new).take(num_workers);
|
let arenas = std::iter::repeat_with(Bump::new).take(num_workers);
|
||||||
let worker_arenas = arena.alloc(bumpalo::collections::Vec::from_iter_in(arenas, arena));
|
let worker_arenas = arena.alloc(bumpalo::collections::Vec::from_iter_in(arenas, arena));
|
||||||
|
|
|
@ -14,4 +14,3 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
snafu = { version = "0.6.10", features = ["backtraces"] }
|
snafu = { version = "0.6.10", features = ["backtraces"] }
|
||||||
arrayvec = "0.7.2"
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ roc_target = { path = "../roc_target" }
|
||||||
roc_error_macros = {path="../../error_macros"}
|
roc_error_macros = {path="../../error_macros"}
|
||||||
roc_debug_flags = {path="../debug_flags"}
|
roc_debug_flags = {path="../debug_flags"}
|
||||||
ven_pretty = { path = "../../vendor/pretty" }
|
ven_pretty = { path = "../../vendor/pretty" }
|
||||||
morphic_lib = { path = "../../vendor/morphic_lib" }
|
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
hashbrown = { version = "0.11.2", features = [ "bumpalo" ] }
|
hashbrown = { version = "0.11.2", features = [ "bumpalo" ] }
|
||||||
ven_graph = { path = "../../vendor/pathfinding" }
|
ven_graph = { path = "../../vendor/pathfinding" }
|
||||||
|
|
|
@ -1210,15 +1210,8 @@ pub fn optimize_when<'a>(
|
||||||
// bind the fields referenced in the pattern. For guards this happens separately, so
|
// bind the fields referenced in the pattern. For guards this happens separately, so
|
||||||
// the pattern variables are defined when evaluating the guard.
|
// the pattern variables are defined when evaluating the guard.
|
||||||
if !has_guard {
|
if !has_guard {
|
||||||
branch = crate::ir::store_pattern(
|
branch =
|
||||||
env,
|
crate::ir::store_pattern(env, procs, layout_cache, &pattern, cond_symbol, branch);
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&pattern,
|
|
||||||
cond_layout,
|
|
||||||
cond_symbol,
|
|
||||||
branch,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ((branch_index, choice), opt_jump) = create_choices(&target_counts, index, branch);
|
let ((branch_index, choice), opt_jump) = create_choices(&target_counts, index, branch);
|
||||||
|
@ -1730,15 +1723,7 @@ fn decide_to_branching<'a>(
|
||||||
body: arena.alloc(decide),
|
body: arena.alloc(decide),
|
||||||
};
|
};
|
||||||
|
|
||||||
crate::ir::store_pattern(
|
crate::ir::store_pattern(env, procs, layout_cache, &pattern, cond_symbol, join)
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&pattern,
|
|
||||||
cond_layout,
|
|
||||||
cond_symbol,
|
|
||||||
join,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Chain {
|
Chain {
|
||||||
test_chain,
|
test_chain,
|
||||||
|
|
|
@ -10,10 +10,12 @@ use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
|
use roc_can::expr::{AnnotatedMark, ClosureData, IntValue};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap};
|
||||||
|
use roc_collections::VecMap;
|
||||||
use roc_debug_flags::{
|
use roc_debug_flags::{
|
||||||
dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
|
dbg_do, ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE,
|
||||||
ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
ROC_PRINT_IR_AFTER_SPECIALIZATION,
|
||||||
};
|
};
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -743,6 +745,157 @@ impl<'a> Specialized<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Uniquely determines the specialization of a polymorphic (non-proc) value symbol.
|
||||||
|
/// Two specializations are equivalent if their [`SpecializationMark`]s are equal.
|
||||||
|
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
|
||||||
|
struct SpecializationMark<'a> {
|
||||||
|
/// The layout of the symbol itself.
|
||||||
|
layout: Layout<'a>,
|
||||||
|
|
||||||
|
/// If this symbol is a closure def, we must also keep track of what function it specializes,
|
||||||
|
/// because the [`layout`] field will only keep track of its closure and lambda set - which can
|
||||||
|
/// be the same for two different function specializations. For example,
|
||||||
|
///
|
||||||
|
/// id = if True then \x -> x else \y -> y
|
||||||
|
/// { a: id "", b: id 1u8 }
|
||||||
|
///
|
||||||
|
/// The lambda set and captures of `id` is the same in both usages inside the record, but the
|
||||||
|
/// reified specializations of `\x -> x` and `\y -> y` must be for Str and U8.
|
||||||
|
///
|
||||||
|
/// Note that this field is not relevant for anything that is not a function.
|
||||||
|
function_mark: Option<RawFunctionLayout<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// When walking a function body, we may encounter specialized usages of polymorphic symbols. For
|
||||||
|
/// example
|
||||||
|
///
|
||||||
|
/// myTag = A
|
||||||
|
/// use1 : [A, B]
|
||||||
|
/// use1 = myTag
|
||||||
|
/// use2 : [A, B, C]
|
||||||
|
/// use2 = myTag
|
||||||
|
///
|
||||||
|
/// We keep track of the specializations of `myTag` and create fresh symbols when there is more
|
||||||
|
/// than one, so that a unique def can be created for each.
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
struct SymbolSpecializations<'a>(
|
||||||
|
// THEORY:
|
||||||
|
// 1. the number of symbols in a def is very small
|
||||||
|
// 2. the number of specializations of a symbol in a def is even smaller (almost always only one)
|
||||||
|
// So, a linear VecMap is preferrable. Use a two-layered one to make (1) extraction of defs easy
|
||||||
|
// and (2) reads of a certain symbol be determined by its first occurrence, not its last.
|
||||||
|
VecMap<Symbol, VecMap<SpecializationMark<'a>, (Variable, Symbol)>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'a> SymbolSpecializations<'a> {
|
||||||
|
/// Gets a specialization for a symbol, or creates a new one.
|
||||||
|
#[inline(always)]
|
||||||
|
fn get_or_insert(
|
||||||
|
&mut self,
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
symbol: Symbol,
|
||||||
|
specialization_var: Variable,
|
||||||
|
) -> Symbol {
|
||||||
|
let arena = env.arena;
|
||||||
|
let subs: &Subs = env.subs;
|
||||||
|
|
||||||
|
let layout = match layout_cache.from_var(arena, specialization_var, subs) {
|
||||||
|
Ok(layout) => layout,
|
||||||
|
// This can happen when the def symbol has a type error. In such cases just use the
|
||||||
|
// def symbol, which is erroring.
|
||||||
|
Err(_) => return symbol,
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_closure = matches!(
|
||||||
|
subs.get_content_without_compacting(specialization_var),
|
||||||
|
Content::Structure(FlatType::Func(..))
|
||||||
|
);
|
||||||
|
let function_mark = if is_closure {
|
||||||
|
let fn_layout = match layout_cache.raw_from_var(arena, specialization_var, subs) {
|
||||||
|
Ok(layout) => layout,
|
||||||
|
// This can happen when the def symbol has a type error. In such cases just use the
|
||||||
|
// def symbol, which is erroring.
|
||||||
|
Err(_) => return symbol,
|
||||||
|
};
|
||||||
|
Some(fn_layout)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let specialization_mark = SpecializationMark {
|
||||||
|
layout,
|
||||||
|
function_mark,
|
||||||
|
};
|
||||||
|
|
||||||
|
let symbol_specializations = self.0.get_or_insert(symbol, Default::default);
|
||||||
|
|
||||||
|
// For the first specialization, always reuse the current symbol. The vast majority of defs
|
||||||
|
// only have one instance type, so this preserves readability of the IR.
|
||||||
|
// TODO: turn me off and see what breaks.
|
||||||
|
let needs_fresh_symbol = !symbol_specializations.is_empty();
|
||||||
|
|
||||||
|
let mut make_specialized_symbol = || {
|
||||||
|
if needs_fresh_symbol {
|
||||||
|
env.unique_symbol()
|
||||||
|
} else {
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_var, specialized_symbol) = symbol_specializations
|
||||||
|
.get_or_insert(specialization_mark, || {
|
||||||
|
(specialization_var, make_specialized_symbol())
|
||||||
|
});
|
||||||
|
|
||||||
|
*specialized_symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a known specialization for a symbol. Returns the overwritten specialization, if any.
|
||||||
|
pub fn get_or_insert_known(
|
||||||
|
&mut self,
|
||||||
|
symbol: Symbol,
|
||||||
|
mark: SpecializationMark<'a>,
|
||||||
|
specialization_var: Variable,
|
||||||
|
specialization_symbol: Symbol,
|
||||||
|
) -> Option<(Variable, Symbol)> {
|
||||||
|
self.0
|
||||||
|
.get_or_insert(symbol, Default::default)
|
||||||
|
.insert(mark, (specialization_var, specialization_symbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes all specializations for a symbol, returning the type and symbol of each specialization.
|
||||||
|
pub fn remove(
|
||||||
|
&mut self,
|
||||||
|
symbol: Symbol,
|
||||||
|
) -> impl ExactSizeIterator<Item = (SpecializationMark<'a>, (Variable, Symbol))> {
|
||||||
|
self.0
|
||||||
|
.remove(&symbol)
|
||||||
|
.map(|(_, specializations)| specializations)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expects and removes at most a single specialization symbol for the given requested symbol.
|
||||||
|
/// A symbol may have no specializations if it is never referenced in a body, so it is possible
|
||||||
|
/// for this to return None.
|
||||||
|
pub fn remove_single(&mut self, symbol: Symbol) -> Option<Symbol> {
|
||||||
|
let mut specializations = self.remove(symbol);
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
specializations.len() < 2,
|
||||||
|
"Symbol {:?} has multiple specializations",
|
||||||
|
symbol
|
||||||
|
);
|
||||||
|
|
||||||
|
specializations.next().map(|(_, (_, symbol))| symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Procs<'a> {
|
pub struct Procs<'a> {
|
||||||
pub partial_procs: PartialProcs<'a>,
|
pub partial_procs: PartialProcs<'a>,
|
||||||
|
@ -753,7 +906,7 @@ pub struct Procs<'a> {
|
||||||
specialized: Specialized<'a>,
|
specialized: Specialized<'a>,
|
||||||
pub runtime_errors: BumpMap<Symbol, &'a str>,
|
pub runtime_errors: BumpMap<Symbol, &'a str>,
|
||||||
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations>,
|
pub externals_we_need: BumpMap<ModuleId, ExternalSpecializations>,
|
||||||
pub needed_symbol_specializations: BumpMap<(Symbol, Layout<'a>), (Variable, Symbol)>,
|
symbol_specializations: SymbolSpecializations<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Procs<'a> {
|
impl<'a> Procs<'a> {
|
||||||
|
@ -767,38 +920,9 @@ impl<'a> Procs<'a> {
|
||||||
specialized: Specialized::default(),
|
specialized: Specialized::default(),
|
||||||
runtime_errors: BumpMap::new_in(arena),
|
runtime_errors: BumpMap::new_in(arena),
|
||||||
externals_we_need: BumpMap::new_in(arena),
|
externals_we_need: BumpMap::new_in(arena),
|
||||||
needed_symbol_specializations: BumpMap::new_in(arena),
|
symbol_specializations: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expects and removes a single specialization symbol for the given requested symbol.
|
|
||||||
/// In debug builds, we assert that the layout of the specialization is the layout expected by
|
|
||||||
/// the requested symbol.
|
|
||||||
fn remove_single_symbol_specialization(
|
|
||||||
&mut self,
|
|
||||||
symbol: Symbol,
|
|
||||||
layout: Layout,
|
|
||||||
) -> Option<Symbol> {
|
|
||||||
let mut specialized_symbols = self
|
|
||||||
.needed_symbol_specializations
|
|
||||||
.drain_filter(|(sym, _), _| sym == &symbol);
|
|
||||||
|
|
||||||
let specialization_symbol = specialized_symbols
|
|
||||||
.next()
|
|
||||||
.map(|((_, specialized_layout), (_, specialized_symbol))| {
|
|
||||||
debug_assert_eq!(specialized_layout, layout, "Requested the single specialization of {:?}, but the specialization layout ({:?}) doesn't match the expected layout ({:?})", symbol, specialized_layout, layout);
|
|
||||||
specialized_symbol
|
|
||||||
});
|
|
||||||
|
|
||||||
debug_assert_eq!(
|
|
||||||
specialized_symbols.count(),
|
|
||||||
0,
|
|
||||||
"Symbol {:?} has multiple specializations",
|
|
||||||
symbol
|
|
||||||
);
|
|
||||||
|
|
||||||
specialization_symbol
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -2166,9 +2290,9 @@ pub fn specialize_all<'a>(
|
||||||
specialize_host_specializations(env, &mut procs, layout_cache, specializations_for_host);
|
specialize_host_specializations(env, &mut procs, layout_cache, specializations_for_host);
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
procs.needed_symbol_specializations.is_empty(),
|
procs.symbol_specializations.is_empty(),
|
||||||
"{:?}",
|
"{:?}",
|
||||||
&procs.needed_symbol_specializations
|
&procs.symbol_specializations
|
||||||
);
|
);
|
||||||
|
|
||||||
procs
|
procs
|
||||||
|
@ -2503,11 +2627,10 @@ fn specialize_external<'a>(
|
||||||
// An argument from the closure list may have taken on a specialized symbol
|
// An argument from the closure list may have taken on a specialized symbol
|
||||||
// name during the evaluation of the def body. If this is the case, load the
|
// name during the evaluation of the def body. If this is the case, load the
|
||||||
// specialized name rather than the original captured name!
|
// specialized name rather than the original captured name!
|
||||||
let mut get_specialized_name = |symbol, layout| {
|
let mut get_specialized_name = |symbol| {
|
||||||
procs
|
procs
|
||||||
.needed_symbol_specializations
|
.symbol_specializations
|
||||||
.remove(&(symbol, layout))
|
.remove_single(symbol)
|
||||||
.map(|(_, specialized)| specialized)
|
|
||||||
.unwrap_or(symbol)
|
.unwrap_or(symbol)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2545,7 +2668,7 @@ fn specialize_external<'a>(
|
||||||
union_layout,
|
union_layout,
|
||||||
};
|
};
|
||||||
|
|
||||||
let symbol = get_specialized_name(**symbol, **layout);
|
let symbol = get_specialized_name(**symbol);
|
||||||
|
|
||||||
specialized_body = Stmt::Let(
|
specialized_body = Stmt::Let(
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -2588,7 +2711,7 @@ fn specialize_external<'a>(
|
||||||
structure: Symbol::ARG_CLOSURE,
|
structure: Symbol::ARG_CLOSURE,
|
||||||
};
|
};
|
||||||
|
|
||||||
let symbol = get_specialized_name(**symbol, **layout);
|
let symbol = get_specialized_name(**symbol);
|
||||||
|
|
||||||
specialized_body = Stmt::Let(
|
specialized_body = Stmt::Let(
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -2633,11 +2756,10 @@ fn specialize_external<'a>(
|
||||||
let proc_args: Vec<_> = proc_args
|
let proc_args: Vec<_> = proc_args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(layout, symbol)| {
|
.map(|&(layout, symbol)| {
|
||||||
|
// Grab the specialization symbol, if it exists.
|
||||||
let symbol = procs
|
let symbol = procs
|
||||||
.needed_symbol_specializations
|
.symbol_specializations
|
||||||
// We can remove the specialization since this is the definition site.
|
.remove_single(symbol)
|
||||||
.remove(&(symbol, layout))
|
|
||||||
.map(|(_, specialized_symbol)| specialized_symbol)
|
|
||||||
.unwrap_or(symbol);
|
.unwrap_or(symbol);
|
||||||
|
|
||||||
(layout, symbol)
|
(layout, symbol)
|
||||||
|
@ -3351,18 +3473,7 @@ pub fn with_hole<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let outer_symbol = env.unique_symbol();
|
let outer_symbol = env.unique_symbol();
|
||||||
let pattern_layout = layout_cache
|
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||||
.from_var(env.arena, def.expr_var, env.subs)
|
|
||||||
.expect("Pattern has no layout");
|
|
||||||
stmt = store_pattern(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&mono_pattern,
|
|
||||||
pattern_layout,
|
|
||||||
outer_symbol,
|
|
||||||
stmt,
|
|
||||||
);
|
|
||||||
|
|
||||||
// convert the def body, store in outer_symbol
|
// convert the def body, store in outer_symbol
|
||||||
with_hole(
|
with_hole(
|
||||||
|
@ -3405,7 +3516,9 @@ pub fn with_hole<'a>(
|
||||||
can_reuse_symbol(env, procs, &roc_can::expr::Expr::Var(symbol))
|
can_reuse_symbol(env, procs, &roc_can::expr::Expr::Var(symbol))
|
||||||
{
|
{
|
||||||
let real_symbol =
|
let real_symbol =
|
||||||
reuse_symbol_or_specialize(env, procs, layout_cache, symbol, variable);
|
procs
|
||||||
|
.symbol_specializations
|
||||||
|
.get_or_insert(env, layout_cache, symbol, variable);
|
||||||
symbol = real_symbol;
|
symbol = real_symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3484,8 +3597,12 @@ pub fn with_hole<'a>(
|
||||||
match can_reuse_symbol(env, procs, &loc_arg_expr.value) {
|
match can_reuse_symbol(env, procs, &loc_arg_expr.value) {
|
||||||
// Opaques decay to their argument.
|
// Opaques decay to their argument.
|
||||||
ReuseSymbol::Value(symbol) => {
|
ReuseSymbol::Value(symbol) => {
|
||||||
let real_name =
|
let real_name = procs.symbol_specializations.get_or_insert(
|
||||||
reuse_symbol_or_specialize(env, procs, layout_cache, symbol, arg_var);
|
env,
|
||||||
|
layout_cache,
|
||||||
|
symbol,
|
||||||
|
arg_var,
|
||||||
|
);
|
||||||
let mut result = hole.clone();
|
let mut result = hole.clone();
|
||||||
substitute_in_exprs(arena, &mut result, assigned, real_name);
|
substitute_in_exprs(arena, &mut result, assigned, real_name);
|
||||||
result
|
result
|
||||||
|
@ -3538,9 +3655,8 @@ pub fn with_hole<'a>(
|
||||||
can_fields.push(Field::Function(symbol, variable));
|
can_fields.push(Field::Function(symbol, variable));
|
||||||
}
|
}
|
||||||
Value(symbol) => {
|
Value(symbol) => {
|
||||||
let reusable = reuse_symbol_or_specialize(
|
let reusable = procs.symbol_specializations.get_or_insert(
|
||||||
env,
|
env,
|
||||||
procs,
|
|
||||||
layout_cache,
|
layout_cache,
|
||||||
symbol,
|
symbol,
|
||||||
field.var,
|
field.var,
|
||||||
|
@ -4353,25 +4469,38 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value(function_symbol) => match full_layout {
|
Value(function_symbol) => {
|
||||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
let function_symbol = procs.symbol_specializations.get_or_insert(
|
||||||
let closure_data_symbol = function_symbol;
|
env,
|
||||||
|
layout_cache,
|
||||||
|
function_symbol,
|
||||||
|
fn_var,
|
||||||
|
);
|
||||||
|
|
||||||
result = match_on_lambda_set(
|
match full_layout {
|
||||||
env,
|
RawFunctionLayout::Function(
|
||||||
lambda_set,
|
|
||||||
closure_data_symbol,
|
|
||||||
arg_symbols,
|
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
|
lambda_set,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
assigned,
|
) => {
|
||||||
hole,
|
let closure_data_symbol = function_symbol;
|
||||||
);
|
|
||||||
|
result = match_on_lambda_set(
|
||||||
|
env,
|
||||||
|
lambda_set,
|
||||||
|
closure_data_symbol,
|
||||||
|
arg_symbols,
|
||||||
|
arg_layouts,
|
||||||
|
ret_layout,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
||||||
|
unreachable!("calling a non-closure layout")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
RawFunctionLayout::ZeroArgumentThunk(_) => {
|
}
|
||||||
unreachable!("calling a non-closure layout")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UnspecializedExpr(symbol) => {
|
UnspecializedExpr(symbol) => {
|
||||||
match procs.ability_member_aliases.get(symbol).unwrap() {
|
match procs.ability_member_aliases.get(symbol).unwrap() {
|
||||||
&AbilityMember(member) => {
|
&AbilityMember(member) => {
|
||||||
|
@ -5521,7 +5650,6 @@ pub fn from_can<'a>(
|
||||||
}
|
}
|
||||||
LetNonRec(def, cont, outer_annotation) => {
|
LetNonRec(def, cont, outer_annotation) => {
|
||||||
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
if let roc_can::pattern::Pattern::Identifier(symbol) = &def.loc_pattern.value {
|
||||||
// dbg!(symbol, &def.loc_expr.value);
|
|
||||||
match def.loc_expr.value {
|
match def.loc_expr.value {
|
||||||
roc_can::expr::Expr::Closure(closure_data) => {
|
roc_can::expr::Expr::Closure(closure_data) => {
|
||||||
register_capturing_closure(env, procs, layout_cache, *symbol, closure_data);
|
register_capturing_closure(env, procs, layout_cache, *symbol, closure_data);
|
||||||
|
@ -5653,12 +5781,14 @@ pub fn from_can<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
let rest = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
let needs_def_specializations = procs
|
// Remove all the requested symbol specializations now, since this is the
|
||||||
.needed_symbol_specializations
|
// def site and hence we won't need them any higher up.
|
||||||
.keys()
|
let mut needed_specializations =
|
||||||
.any(|(s, _)| s == symbol);
|
procs.symbol_specializations.remove(*symbol);
|
||||||
|
|
||||||
if !needs_def_specializations {
|
if needed_specializations.len() == 0 {
|
||||||
|
// We don't need any specializations, that means this symbol is never
|
||||||
|
// referenced.
|
||||||
return with_hole(
|
return with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
|
@ -5674,16 +5804,9 @@ pub fn from_can<'a>(
|
||||||
|
|
||||||
let mut stmt = rest;
|
let mut stmt = rest;
|
||||||
|
|
||||||
// Remove all the requested symbol specializations now, since this is the
|
|
||||||
// def site and hence we won't need them any higher up.
|
|
||||||
let mut needed_specializations = procs
|
|
||||||
.needed_symbol_specializations
|
|
||||||
.drain_filter(|(s, _), _| s == symbol)
|
|
||||||
.collect::<std::vec::Vec<_>>();
|
|
||||||
|
|
||||||
if needed_specializations.len() == 1 {
|
if needed_specializations.len() == 1 {
|
||||||
let ((_, _wanted_layout), (var, specialized_symbol)) =
|
let (_specialization_mark, (var, specialized_symbol)) =
|
||||||
needed_specializations.pop().unwrap();
|
needed_specializations.next().unwrap();
|
||||||
|
|
||||||
// Unify the expr_var with the requested specialization once.
|
// Unify the expr_var with the requested specialization once.
|
||||||
let _res =
|
let _res =
|
||||||
|
@ -5700,7 +5823,7 @@ pub fn from_can<'a>(
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Need to eat the cost and create a specialized version of the body for each specialization.
|
// Need to eat the cost and create a specialized version of the body for each specialization.
|
||||||
for ((_original_symbol, _wanted_layout), (var, specialized_symbol)) in
|
for (_specialization_mark, (var, specialized_symbol)) in
|
||||||
needed_specializations
|
needed_specializations
|
||||||
{
|
{
|
||||||
use crate::copy::deep_copy_type_vars_into_expr;
|
use crate::copy::deep_copy_type_vars_into_expr;
|
||||||
|
@ -5744,89 +5867,50 @@ pub fn from_can<'a>(
|
||||||
Err(_) => todo!(),
|
Err(_) => todo!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Pattern::Identifier(symbol) = mono_pattern {
|
if let Pattern::Identifier(_symbol) = mono_pattern {
|
||||||
let mut hole =
|
internal_error!("Identifier patterns should be handled in a higher code pass!")
|
||||||
env.arena
|
}
|
||||||
.alloc(from_can(env, variable, cont.value, procs, layout_cache));
|
|
||||||
|
|
||||||
for (symbol, variable, expr) in assignments {
|
// convert the continuation
|
||||||
let stmt = with_hole(env, expr, variable, procs, layout_cache, symbol, hole);
|
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
|
||||||
hole = env.arena.alloc(stmt);
|
// layer on any default record fields
|
||||||
}
|
for (symbol, variable, expr) in assignments {
|
||||||
|
let specialization_symbol = procs
|
||||||
|
.symbol_specializations
|
||||||
|
.remove_single(symbol)
|
||||||
|
// Can happen when the symbol was never used under this body, and hence has no
|
||||||
|
// requested specialization.
|
||||||
|
.unwrap_or(symbol);
|
||||||
|
|
||||||
|
let hole = env.arena.alloc(stmt);
|
||||||
|
stmt = with_hole(
|
||||||
|
env,
|
||||||
|
expr,
|
||||||
|
variable,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
specialization_symbol,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
||||||
|
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||||
|
} else {
|
||||||
|
let outer_symbol = env.unique_symbol();
|
||||||
|
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||||
|
|
||||||
|
// convert the def body, store in outer_symbol
|
||||||
with_hole(
|
with_hole(
|
||||||
env,
|
env,
|
||||||
def.loc_expr.value,
|
def.loc_expr.value,
|
||||||
def.expr_var,
|
def.expr_var,
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
symbol,
|
outer_symbol,
|
||||||
hole,
|
env.arena.alloc(stmt),
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
// convert the continuation
|
|
||||||
let mut stmt = from_can(env, variable, cont.value, procs, layout_cache);
|
|
||||||
|
|
||||||
// layer on any default record fields
|
|
||||||
for (symbol, variable, expr) in assignments {
|
|
||||||
let layout = layout_cache
|
|
||||||
.from_var(env.arena, variable, env.subs)
|
|
||||||
.expect("Default field has no layout");
|
|
||||||
let specialization_symbol = procs
|
|
||||||
.remove_single_symbol_specialization(symbol, layout)
|
|
||||||
// Can happen when the symbol was never used under this body, and hence has no
|
|
||||||
// requested specialization.
|
|
||||||
.unwrap_or(symbol);
|
|
||||||
|
|
||||||
let hole = env.arena.alloc(stmt);
|
|
||||||
stmt = with_hole(
|
|
||||||
env,
|
|
||||||
expr,
|
|
||||||
variable,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
specialization_symbol,
|
|
||||||
hole,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let pattern_layout = layout_cache
|
|
||||||
.from_var(env.arena, def.expr_var, env.subs)
|
|
||||||
.expect("Pattern has no layout");
|
|
||||||
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
|
||||||
store_pattern(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&mono_pattern,
|
|
||||||
pattern_layout,
|
|
||||||
outer_symbol,
|
|
||||||
stmt,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let outer_symbol = env.unique_symbol();
|
|
||||||
stmt = store_pattern(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
&mono_pattern,
|
|
||||||
pattern_layout,
|
|
||||||
outer_symbol,
|
|
||||||
stmt,
|
|
||||||
);
|
|
||||||
|
|
||||||
// convert the def body, store in outer_symbol
|
|
||||||
with_hole(
|
|
||||||
env,
|
|
||||||
def.loc_expr.value,
|
|
||||||
def.expr_var,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
outer_symbol,
|
|
||||||
env.arena.alloc(stmt),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6380,19 +6464,10 @@ pub fn store_pattern<'a>(
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
can_pat: &Pattern<'a>,
|
can_pat: &Pattern<'a>,
|
||||||
pattern_layout: Layout,
|
|
||||||
outer_symbol: Symbol,
|
outer_symbol: Symbol,
|
||||||
stmt: Stmt<'a>,
|
stmt: Stmt<'a>,
|
||||||
) -> Stmt<'a> {
|
) -> Stmt<'a> {
|
||||||
match store_pattern_help(
|
match store_pattern_help(env, procs, layout_cache, can_pat, outer_symbol, stmt) {
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
can_pat,
|
|
||||||
pattern_layout,
|
|
||||||
outer_symbol,
|
|
||||||
stmt,
|
|
||||||
) {
|
|
||||||
StorePattern::Productive(new) => new,
|
StorePattern::Productive(new) => new,
|
||||||
StorePattern::NotProductive(new) => new,
|
StorePattern::NotProductive(new) => new,
|
||||||
}
|
}
|
||||||
|
@ -6412,7 +6487,6 @@ fn store_pattern_help<'a>(
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
can_pat: &Pattern<'a>,
|
can_pat: &Pattern<'a>,
|
||||||
pattern_layout: Layout,
|
|
||||||
outer_symbol: Symbol,
|
outer_symbol: Symbol,
|
||||||
mut stmt: Stmt<'a>,
|
mut stmt: Stmt<'a>,
|
||||||
) -> StorePattern<'a> {
|
) -> StorePattern<'a> {
|
||||||
|
@ -6423,7 +6497,8 @@ fn store_pattern_help<'a>(
|
||||||
// An identifier in a pattern can define at most one specialization!
|
// An identifier in a pattern can define at most one specialization!
|
||||||
// Remove any requested specializations for this name now, since this is the definition site.
|
// Remove any requested specializations for this name now, since this is the definition site.
|
||||||
let specialization_symbol = procs
|
let specialization_symbol = procs
|
||||||
.remove_single_symbol_specialization(*symbol, pattern_layout)
|
.symbol_specializations
|
||||||
|
.remove_single(*symbol)
|
||||||
// Can happen when the symbol was never used under this body, and hence has no
|
// Can happen when the symbol was never used under this body, and hence has no
|
||||||
// requested specialization.
|
// requested specialization.
|
||||||
.unwrap_or(*symbol);
|
.unwrap_or(*symbol);
|
||||||
|
@ -6444,16 +6519,8 @@ fn store_pattern_help<'a>(
|
||||||
return StorePattern::NotProductive(stmt);
|
return StorePattern::NotProductive(stmt);
|
||||||
}
|
}
|
||||||
NewtypeDestructure { arguments, .. } => match arguments.as_slice() {
|
NewtypeDestructure { arguments, .. } => match arguments.as_slice() {
|
||||||
[(pattern, layout)] => {
|
[(pattern, _layout)] => {
|
||||||
return store_pattern_help(
|
return store_pattern_help(env, procs, layout_cache, pattern, outer_symbol, stmt);
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
pattern,
|
|
||||||
*layout,
|
|
||||||
outer_symbol,
|
|
||||||
stmt,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut fields = Vec::with_capacity_in(arguments.len(), env.arena);
|
let mut fields = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
|
@ -6490,16 +6557,8 @@ fn store_pattern_help<'a>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
OpaqueUnwrap { argument, .. } => {
|
OpaqueUnwrap { argument, .. } => {
|
||||||
let (pattern, layout) = &**argument;
|
let (pattern, _layout) = &**argument;
|
||||||
return store_pattern_help(
|
return store_pattern_help(env, procs, layout_cache, pattern, outer_symbol, stmt);
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
pattern,
|
|
||||||
*layout,
|
|
||||||
outer_symbol,
|
|
||||||
stmt,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordDestructure(destructs, [_single_field]) => {
|
RecordDestructure(destructs, [_single_field]) => {
|
||||||
|
@ -6507,7 +6566,8 @@ fn store_pattern_help<'a>(
|
||||||
match &destruct.typ {
|
match &destruct.typ {
|
||||||
DestructType::Required(symbol) => {
|
DestructType::Required(symbol) => {
|
||||||
let specialization_symbol = procs
|
let specialization_symbol = procs
|
||||||
.remove_single_symbol_specialization(*symbol, destruct.layout)
|
.symbol_specializations
|
||||||
|
.remove_single(*symbol)
|
||||||
// Can happen when the symbol was never used under this body, and hence has no
|
// Can happen when the symbol was never used under this body, and hence has no
|
||||||
// requested specialization.
|
// requested specialization.
|
||||||
.unwrap_or(*symbol);
|
.unwrap_or(*symbol);
|
||||||
|
@ -6525,7 +6585,6 @@ fn store_pattern_help<'a>(
|
||||||
procs,
|
procs,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
guard_pattern,
|
guard_pattern,
|
||||||
destruct.layout,
|
|
||||||
outer_symbol,
|
outer_symbol,
|
||||||
stmt,
|
stmt,
|
||||||
);
|
);
|
||||||
|
@ -6598,7 +6657,8 @@ fn store_tag_pattern<'a>(
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
// Pattern can define only one specialization
|
// Pattern can define only one specialization
|
||||||
let symbol = procs
|
let symbol = procs
|
||||||
.remove_single_symbol_specialization(*symbol, arg_layout)
|
.symbol_specializations
|
||||||
|
.remove_single(*symbol)
|
||||||
.unwrap_or(*symbol);
|
.unwrap_or(*symbol);
|
||||||
|
|
||||||
// store immediately in the given symbol
|
// store immediately in the given symbol
|
||||||
|
@ -6619,15 +6679,7 @@ fn store_tag_pattern<'a>(
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
|
|
||||||
// first recurse, continuing to unpack symbol
|
// first recurse, continuing to unpack symbol
|
||||||
match store_pattern_help(
|
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
argument,
|
|
||||||
arg_layout,
|
|
||||||
symbol,
|
|
||||||
stmt,
|
|
||||||
) {
|
|
||||||
StorePattern::Productive(new) => {
|
StorePattern::Productive(new) => {
|
||||||
is_productive = true;
|
is_productive = true;
|
||||||
stmt = new;
|
stmt = new;
|
||||||
|
@ -6687,7 +6739,8 @@ fn store_newtype_pattern<'a>(
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
// store immediately in the given symbol, removing it specialization if it had any
|
// store immediately in the given symbol, removing it specialization if it had any
|
||||||
let specialization_symbol = procs
|
let specialization_symbol = procs
|
||||||
.remove_single_symbol_specialization(*symbol, arg_layout)
|
.symbol_specializations
|
||||||
|
.remove_single(*symbol)
|
||||||
// Can happen when the symbol was never used under this body, and hence has no
|
// Can happen when the symbol was never used under this body, and hence has no
|
||||||
// requested specialization.
|
// requested specialization.
|
||||||
.unwrap_or(*symbol);
|
.unwrap_or(*symbol);
|
||||||
|
@ -6714,15 +6767,7 @@ fn store_newtype_pattern<'a>(
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
|
|
||||||
// first recurse, continuing to unpack symbol
|
// first recurse, continuing to unpack symbol
|
||||||
match store_pattern_help(
|
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
argument,
|
|
||||||
arg_layout,
|
|
||||||
symbol,
|
|
||||||
stmt,
|
|
||||||
) {
|
|
||||||
StorePattern::Productive(new) => {
|
StorePattern::Productive(new) => {
|
||||||
is_productive = true;
|
is_productive = true;
|
||||||
stmt = new;
|
stmt = new;
|
||||||
|
@ -6770,7 +6815,8 @@ fn store_record_destruct<'a>(
|
||||||
// A destructure can define at most one specialization!
|
// A destructure can define at most one specialization!
|
||||||
// Remove any requested specializations for this name now, since this is the definition site.
|
// Remove any requested specializations for this name now, since this is the definition site.
|
||||||
let specialization_symbol = procs
|
let specialization_symbol = procs
|
||||||
.remove_single_symbol_specialization(*symbol, destruct.layout)
|
.symbol_specializations
|
||||||
|
.remove_single(*symbol)
|
||||||
// Can happen when the symbol was never used under this body, and hence has no
|
// Can happen when the symbol was never used under this body, and hence has no
|
||||||
// requested specialization.
|
// requested specialization.
|
||||||
.unwrap_or(*symbol);
|
.unwrap_or(*symbol);
|
||||||
|
@ -6785,7 +6831,8 @@ fn store_record_destruct<'a>(
|
||||||
DestructType::Guard(guard_pattern) => match &guard_pattern {
|
DestructType::Guard(guard_pattern) => match &guard_pattern {
|
||||||
Identifier(symbol) => {
|
Identifier(symbol) => {
|
||||||
let specialization_symbol = procs
|
let specialization_symbol = procs
|
||||||
.remove_single_symbol_specialization(*symbol, destruct.layout)
|
.symbol_specializations
|
||||||
|
.remove_single(*symbol)
|
||||||
// Can happen when the symbol was never used under this body, and hence has no
|
// Can happen when the symbol was never used under this body, and hence has no
|
||||||
// requested specialization.
|
// requested specialization.
|
||||||
.unwrap_or(*symbol);
|
.unwrap_or(*symbol);
|
||||||
|
@ -6823,15 +6870,7 @@ fn store_record_destruct<'a>(
|
||||||
_ => {
|
_ => {
|
||||||
let symbol = env.unique_symbol();
|
let symbol = env.unique_symbol();
|
||||||
|
|
||||||
match store_pattern_help(
|
match store_pattern_help(env, procs, layout_cache, guard_pattern, symbol, stmt) {
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
guard_pattern,
|
|
||||||
destruct.layout,
|
|
||||||
symbol,
|
|
||||||
stmt,
|
|
||||||
) {
|
|
||||||
StorePattern::Productive(new) => {
|
StorePattern::Productive(new) => {
|
||||||
stmt = new;
|
stmt = new;
|
||||||
stmt = Stmt::Let(symbol, load, destruct.layout, env.arena.alloc(stmt));
|
stmt = Stmt::Let(symbol, load, destruct.layout, env.arena.alloc(stmt));
|
||||||
|
@ -6894,45 +6933,6 @@ fn can_reuse_symbol<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reuses the specialized symbol for a given symbol and instance type. If no specialization symbol
|
|
||||||
/// yet exists, one is created.
|
|
||||||
fn reuse_symbol_or_specialize<'a>(
|
|
||||||
env: &mut Env<'a, '_>,
|
|
||||||
procs: &mut Procs<'a>,
|
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
|
||||||
symbol: Symbol,
|
|
||||||
var: Variable,
|
|
||||||
) -> Symbol {
|
|
||||||
let wanted_layout = match layout_cache.from_var(env.arena, var, env.subs) {
|
|
||||||
Ok(layout) => layout,
|
|
||||||
// This can happen when the def symbol has a type error. In such cases just use the
|
|
||||||
// def symbol, which is erroring.
|
|
||||||
Err(_) => return symbol,
|
|
||||||
};
|
|
||||||
|
|
||||||
// For the first specialization, always reuse the current symbol. The vast majority of defs
|
|
||||||
// only have one instance type, so this preserves readability of the IR.
|
|
||||||
let needs_fresh_symbol = procs
|
|
||||||
.needed_symbol_specializations
|
|
||||||
.keys()
|
|
||||||
.any(|(s, _)| *s == symbol);
|
|
||||||
|
|
||||||
let mut make_specialized_symbol = || {
|
|
||||||
if needs_fresh_symbol {
|
|
||||||
env.unique_symbol()
|
|
||||||
} else {
|
|
||||||
symbol
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (_, specialized_symbol) = procs
|
|
||||||
.needed_symbol_specializations
|
|
||||||
.entry((symbol, wanted_layout))
|
|
||||||
.or_insert_with(|| (var, make_specialized_symbol()));
|
|
||||||
|
|
||||||
*specialized_symbol
|
|
||||||
}
|
|
||||||
|
|
||||||
fn possible_reuse_symbol_or_specialize<'a>(
|
fn possible_reuse_symbol_or_specialize<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
@ -6942,7 +6942,9 @@ fn possible_reuse_symbol_or_specialize<'a>(
|
||||||
) -> Symbol {
|
) -> Symbol {
|
||||||
match can_reuse_symbol(env, procs, expr) {
|
match can_reuse_symbol(env, procs, expr) {
|
||||||
ReuseSymbol::Value(symbol) => {
|
ReuseSymbol::Value(symbol) => {
|
||||||
reuse_symbol_or_specialize(env, procs, layout_cache, symbol, var)
|
procs
|
||||||
|
.symbol_specializations
|
||||||
|
.get_or_insert(env, layout_cache, symbol, var)
|
||||||
}
|
}
|
||||||
_ => env.unique_symbol(),
|
_ => env.unique_symbol(),
|
||||||
}
|
}
|
||||||
|
@ -6987,16 +6989,13 @@ where
|
||||||
let result = build_rest(env, procs, layout_cache);
|
let result = build_rest(env, procs, layout_cache);
|
||||||
|
|
||||||
// The specializations we wanted of the symbol on the LHS of this alias.
|
// The specializations we wanted of the symbol on the LHS of this alias.
|
||||||
let needed_specializations_of_left = procs
|
let needed_specializations_of_left = procs.symbol_specializations.remove(left);
|
||||||
.needed_symbol_specializations
|
|
||||||
.drain_filter(|(s, _), _| s == &left)
|
|
||||||
.collect::<std::vec::Vec<_>>();
|
|
||||||
|
|
||||||
if procs.is_imported_module_thunk(right) {
|
if procs.is_imported_module_thunk(right) {
|
||||||
// if this is an imported symbol, then we must make sure it is
|
// if this is an imported symbol, then we must make sure it is
|
||||||
// specialized, and wrap the original in a function pointer.
|
// specialized, and wrap the original in a function pointer.
|
||||||
let mut result = result;
|
let mut result = result;
|
||||||
for (_, (variable, left)) in needed_specializations_of_left.into_iter() {
|
for (_, (variable, left)) in needed_specializations_of_left {
|
||||||
add_needed_external(procs, env, variable, right);
|
add_needed_external(procs, env, variable, right);
|
||||||
|
|
||||||
let res_layout = layout_cache.from_var(env.arena, variable, env.subs);
|
let res_layout = layout_cache.from_var(env.arena, variable, env.subs);
|
||||||
|
@ -7016,14 +7015,17 @@ where
|
||||||
// We need to lift all specializations of "left" to be specializations of "right".
|
// We need to lift all specializations of "left" to be specializations of "right".
|
||||||
let mut scratchpad_update_specializations = std::vec::Vec::new();
|
let mut scratchpad_update_specializations = std::vec::Vec::new();
|
||||||
|
|
||||||
let left_had_specialization_symbols = !needed_specializations_of_left.is_empty();
|
let left_had_specialization_symbols = needed_specializations_of_left.len() > 0;
|
||||||
|
|
||||||
for ((_, layout), (specialized_var, specialized_sym)) in
|
for (specialization_mark, (specialized_var, specialized_sym)) in
|
||||||
needed_specializations_of_left.into_iter()
|
needed_specializations_of_left
|
||||||
{
|
{
|
||||||
let old_specialized_sym = procs
|
let old_specialized_sym = procs.symbol_specializations.get_or_insert_known(
|
||||||
.needed_symbol_specializations
|
right,
|
||||||
.insert((right, layout), (specialized_var, specialized_sym));
|
specialization_mark,
|
||||||
|
specialized_var,
|
||||||
|
specialized_sym,
|
||||||
|
);
|
||||||
|
|
||||||
if let Some((_, old_specialized_sym)) = old_specialized_sym {
|
if let Some((_, old_specialized_sym)) = old_specialized_sym {
|
||||||
scratchpad_update_specializations.push((old_specialized_sym, specialized_sym));
|
scratchpad_update_specializations.push((old_specialized_sym, specialized_sym));
|
||||||
|
|
|
@ -476,10 +476,13 @@ impl Pools {
|
||||||
self.0.iter()
|
self.0.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split_last(&self) -> (&Vec<Variable>, &[Vec<Variable>]) {
|
pub fn split_last(mut self) -> (Vec<Variable>, Vec<Vec<Variable>>) {
|
||||||
self.0
|
let last = self
|
||||||
.split_last()
|
.0
|
||||||
.unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools"))
|
.pop()
|
||||||
|
.unwrap_or_else(|| panic!("Attempted to split_last() on non-empty Pools"));
|
||||||
|
|
||||||
|
(last, self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extend_to(&mut self, n: usize) {
|
pub fn extend_to(&mut self, n: usize) {
|
||||||
|
@ -737,8 +740,7 @@ fn solve(
|
||||||
|
|
||||||
// pop pool
|
// pop pool
|
||||||
generalize(subs, young_mark, visit_mark, next_rank, pools);
|
generalize(subs, young_mark, visit_mark, next_rank, pools);
|
||||||
|
debug_assert!(pools.get(next_rank).is_empty());
|
||||||
pools.get_mut(next_rank).clear();
|
|
||||||
|
|
||||||
// check that things went well
|
// check that things went well
|
||||||
dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, {
|
dbg_do!(ROC_VERIFY_RIGID_LET_GENERALIZED, {
|
||||||
|
@ -2426,7 +2428,7 @@ fn generalize(
|
||||||
young_rank: Rank,
|
young_rank: Rank,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
) {
|
) {
|
||||||
let young_vars = pools.get(young_rank);
|
let young_vars = std::mem::take(pools.get_mut(young_rank));
|
||||||
let rank_table = pool_to_rank_table(subs, young_mark, young_rank, young_vars);
|
let rank_table = pool_to_rank_table(subs, young_mark, young_rank, young_vars);
|
||||||
|
|
||||||
// Get the ranks right for each entry.
|
// Get the ranks right for each entry.
|
||||||
|
@ -2437,12 +2439,12 @@ fn generalize(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (last_pool, all_but_last_pool) = rank_table.split_last();
|
let (mut last_pool, all_but_last_pool) = rank_table.split_last();
|
||||||
|
|
||||||
// For variables that have rank lowerer than young_rank, register them in
|
// For variables that have rank lowerer than young_rank, register them in
|
||||||
// the appropriate old pool if they are not redundant.
|
// the appropriate old pool if they are not redundant.
|
||||||
for vars in all_but_last_pool {
|
for vars in all_but_last_pool {
|
||||||
for &var in vars {
|
for var in vars {
|
||||||
if !subs.redundant(var) {
|
if !subs.redundant(var) {
|
||||||
let rank = subs.get_rank(var);
|
let rank = subs.get_rank(var);
|
||||||
|
|
||||||
|
@ -2453,7 +2455,7 @@ fn generalize(
|
||||||
|
|
||||||
// For variables with rank young_rank, if rank < young_rank: register in old pool,
|
// For variables with rank young_rank, if rank < young_rank: register in old pool,
|
||||||
// otherwise generalize
|
// otherwise generalize
|
||||||
for &var in last_pool {
|
for var in last_pool.drain(..) {
|
||||||
if !subs.redundant(var) {
|
if !subs.redundant(var) {
|
||||||
let desc_rank = subs.get_rank(var);
|
let desc_rank = subs.get_rank(var);
|
||||||
|
|
||||||
|
@ -2464,32 +2466,38 @@ fn generalize(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// re-use the last_vector (which likely has a good capacity for future runs
|
||||||
|
*pools.get_mut(young_rank) = last_pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sort the variables into buckets by rank.
|
/// Sort the variables into buckets by rank.
|
||||||
|
#[inline]
|
||||||
fn pool_to_rank_table(
|
fn pool_to_rank_table(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
young_mark: Mark,
|
young_mark: Mark,
|
||||||
young_rank: Rank,
|
young_rank: Rank,
|
||||||
young_vars: &[Variable],
|
mut young_vars: Vec<Variable>,
|
||||||
) -> Pools {
|
) -> Pools {
|
||||||
let mut pools = Pools::new(young_rank.into_usize() + 1);
|
let mut pools = Pools::new(young_rank.into_usize() + 1);
|
||||||
|
|
||||||
// the vast majority of young variables have young_rank
|
// the vast majority of young variables have young_rank
|
||||||
// using `retain` here prevents many `pools.get_mut(young_rank)` lookups
|
let mut i = 0;
|
||||||
let mut young_vars = young_vars.to_vec();
|
while i < young_vars.len() {
|
||||||
young_vars.retain(|var| {
|
let var = young_vars[i];
|
||||||
let rank = subs.get_rank_set_mark(*var, young_mark);
|
let rank = subs.get_rank_set_mark(var, young_mark);
|
||||||
|
|
||||||
if rank != young_rank {
|
if rank != young_rank {
|
||||||
debug_assert!(rank.into_usize() < young_rank.into_usize() + 1);
|
debug_assert!(rank.into_usize() < young_rank.into_usize() + 1);
|
||||||
|
|
||||||
pools.get_mut(rank).push(*var);
|
pools.get_mut(rank).push(var);
|
||||||
false
|
|
||||||
|
// swap an element in; don't increment i
|
||||||
|
young_vars.swap_remove(i);
|
||||||
} else {
|
} else {
|
||||||
true
|
i += 1;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
std::mem::swap(pools.get_mut(young_rank), &mut young_vars);
|
std::mem::swap(pools.get_mut(young_rank), &mut young_vars);
|
||||||
|
|
||||||
|
|
|
@ -6234,4 +6234,22 @@ mod solve_expr {
|
||||||
"F b -> b",
|
"F b -> b",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alias_in_opaque() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ foo ] to "./platform"
|
||||||
|
|
||||||
|
MyError : [ Error ]
|
||||||
|
|
||||||
|
MyResult := Result U8 MyError
|
||||||
|
|
||||||
|
foo = @MyResult (Err Error)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"MyResult",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3270,3 +3270,51 @@ fn dec_float_suffix() {
|
||||||
i128
|
i128
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn ceiling_to_u32() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
n : U32
|
||||||
|
n = Num.ceiling 124.5
|
||||||
|
n
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
125,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn floor_to_u32() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
n : U32
|
||||||
|
n = Num.floor 124.5
|
||||||
|
n
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
124,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn round_to_u32() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
n : U32
|
||||||
|
n = Num.round 124.49
|
||||||
|
n
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
124,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -3267,7 +3267,6 @@ fn polymophic_expression_captured_inside_closure() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
#[ignore = "Compile polymorphic functions"]
|
|
||||||
fn issue_2322() {
|
fn issue_2322() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -3421,7 +3420,6 @@ fn polymorphic_def_used_in_closure() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
#[ignore = "This still doesn't work... yet"]
|
|
||||||
fn polymorphic_lambda_set_usage() {
|
fn polymorphic_lambda_set_usage() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -3429,6 +3427,7 @@ fn polymorphic_lambda_set_usage() {
|
||||||
id1 = \x -> x
|
id1 = \x -> x
|
||||||
id2 = \y -> y
|
id2 = \y -> y
|
||||||
id = if True then id1 else id2
|
id = if True then id1 else id2
|
||||||
|
|
||||||
id 9u8
|
id 9u8
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -3436,3 +3435,21 @@ fn polymorphic_lambda_set_usage() {
|
||||||
u8
|
u8
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn polymorphic_lambda_set_multiple_specializations() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
id1 = \x -> x
|
||||||
|
id2 = \y -> y
|
||||||
|
id = if True then id1 else id2
|
||||||
|
|
||||||
|
(id 9u8) + Num.toU8 (id 16u16)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
25,
|
||||||
|
u8
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ fn create_llvm_module<'a>(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
target_info,
|
target_info,
|
||||||
RenderTarget::ColorTerminal,
|
RenderTarget::ColorTerminal,
|
||||||
Threading::Multi,
|
Threading::AllAvailable,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut loaded = match loaded {
|
let mut loaded = match loaded {
|
||||||
|
|
|
@ -19,6 +19,5 @@ roc_mono = { path = "../mono" }
|
||||||
roc_target = { path = "../roc_target" }
|
roc_target = { path = "../roc_target" }
|
||||||
roc_reporting = { path = "../../reporting" }
|
roc_reporting = { path = "../../reporting" }
|
||||||
test_mono_macros = { path = "../test_mono_macros" }
|
test_mono_macros = { path = "../test_mono_macros" }
|
||||||
pretty_assertions = "1.0.0"
|
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
|
|
|
@ -1123,8 +1123,17 @@ impl Type {
|
||||||
ext.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
ext.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DelayedAlias(AliasCommon { .. }) => {
|
DelayedAlias(AliasCommon {
|
||||||
// do nothing, yay
|
type_arguments,
|
||||||
|
lambda_set_variables,
|
||||||
|
symbol: _,
|
||||||
|
}) => {
|
||||||
|
debug_assert!(lambda_set_variables
|
||||||
|
.iter()
|
||||||
|
.all(|lambda_set| matches!(lambda_set.0, Type::Variable(..))));
|
||||||
|
type_arguments.iter_mut().for_each(|t| {
|
||||||
|
t.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
HostExposedAlias {
|
HostExposedAlias {
|
||||||
type_arguments: type_args,
|
type_arguments: type_args,
|
||||||
|
|
|
@ -28,6 +28,3 @@ peg = "0.8.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
tempfile = "3.2.0"
|
|
||||||
uuid = { version = "0.8.2", features = ["v4"] }
|
|
||||||
indoc = "1.0.3"
|
|
||||||
|
|
|
@ -435,7 +435,7 @@ pub fn load_modules_for_files(filenames: Vec<PathBuf>) -> Vec<LoadedModule> {
|
||||||
Default::default(),
|
Default::default(),
|
||||||
roc_target::TargetInfo::default_x86_64(), // This is just type-checking for docs, so "target" doesn't matter
|
roc_target::TargetInfo::default_x86_64(), // This is just type-checking for docs, so "target" doesn't matter
|
||||||
roc_reporting::report::RenderTarget::ColorTerminal,
|
roc_reporting::report::RenderTarget::ColorTerminal,
|
||||||
Threading::Multi,
|
Threading::AllAvailable,
|
||||||
) {
|
) {
|
||||||
Ok(loaded) => modules.push(loaded),
|
Ok(loaded) => modules.push(loaded),
|
||||||
Err(LoadingProblem::FormattedReport(report)) => {
|
Err(LoadingProblem::FormattedReport(report)) => {
|
||||||
|
|
|
@ -129,7 +129,7 @@ fn run_event_loop(project_dir_path_opt: Option<&Path>) -> Result<(), Box<dyn Err
|
||||||
|
|
||||||
let file_path = Path::new(&file_path_str);
|
let file_path = Path::new(&file_path_str);
|
||||||
|
|
||||||
let loaded_module = load_module(file_path, Threading::Multi);
|
let loaded_module = load_module(file_path, Threading::AllAvailable);
|
||||||
|
|
||||||
let mut var_store = VarStore::default();
|
let mut var_store = VarStore::default();
|
||||||
let dep_idents = IdentIds::exposed_builtins(8);
|
let dep_idents = IdentIds::exposed_builtins(8);
|
||||||
|
|
|
@ -330,7 +330,7 @@ pub mod test_ed_model {
|
||||||
writeln!(file, "{}", clean_code_str)
|
writeln!(file, "{}", clean_code_str)
|
||||||
.unwrap_or_else(|_| panic!("Failed to write {:?} to file: {:?}", clean_code_str, file));
|
.unwrap_or_else(|_| panic!("Failed to write {:?} to file: {:?}", clean_code_str, file));
|
||||||
|
|
||||||
let loaded_module = load_module(&temp_file_full_path, Threading::Multi);
|
let loaded_module = load_module(&temp_file_full_path, Threading::AllAvailable);
|
||||||
|
|
||||||
let mut ed_model = init_dummy_model(
|
let mut ed_model = init_dummy_model(
|
||||||
clean_code_str,
|
clean_code_str,
|
||||||
|
|
|
@ -74,7 +74,9 @@ pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_main() -> i32 {
|
pub extern "C" fn rust_main() -> i32 {
|
||||||
let arg = env::args().nth(1).unwrap();
|
let arg = env::args()
|
||||||
|
.nth(1)
|
||||||
|
.expect("Please pass a .false file as a command-line argument to the false interpreter!");
|
||||||
let arg = RocStr::from(arg.as_str());
|
let arg = RocStr::from(arg.as_str());
|
||||||
|
|
||||||
let size = unsafe { roc_main_size() } as usize;
|
let size = unsafe { roc_main_size() } as usize;
|
||||||
|
|
|
@ -29,4 +29,3 @@ roc_target = { path = "../compiler/roc_target" }
|
||||||
roc_test_utils = { path = "../test_utils" }
|
roc_test_utils = { path = "../test_utils" }
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
tempfile = "3.2.0"
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue