Merge remote-tracking branch 'origin/trunk' into update_zig_09

This commit is contained in:
Folkert 2022-02-18 22:39:44 +01:00
commit 6631845a3c
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
60 changed files with 1597 additions and 1032 deletions

8
.reuse/dep5 Normal file
View file

@ -0,0 +1,8 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Roc
Upstream-Contact: Richard Feldman <oss@rtfeldman.com>
Source: https://github.com/rtfeldman/roc
Files: *
Copyright: © The Roc Contributors
License: UPL-1.0

View file

@ -63,3 +63,4 @@ Matthias Devlamynck <matthias.devlamynck@mailoo.org>
Jan Van Bruggen <JanCVanB@users.noreply.github.com> Jan Van Bruggen <JanCVanB@users.noreply.github.com>
Mats Sigge <<mats.sigge@gmail.com>> Mats Sigge <<mats.sigge@gmail.com>>
Drew Lazzeri <dlazzeri1@gmail.com> Drew Lazzeri <dlazzeri1@gmail.com>
Tom Dohrmann <erbse.13@gmx.de>

View file

@ -19,6 +19,8 @@ Earthly may temporarily use a lot of disk space, up to 90 GB. This disk space is
## Contribution Tips ## Contribution Tips
- Create an issue if the purpose of a struct/field/type/function/... is not immediately clear from its name or nearby comments.
- You find good first issues [here](https://github.com/rtfeldman/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
- Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation. - Before making your first pull request, definitely talk to an existing contributor on [Roc Zulip](https://roc.zulipchat.com) first about what you plan to do! This can not only avoid duplicated effort, it can also avoid making a whole PR only to discover it won't be accepted because the change doesn't fit with the goals of the language's design or implementation.
- It's a good idea to open a work-in-progress pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Be sure to include "WIP" in the title of the PR as long as it's not ready for review! - It's a good idea to open a work-in-progress pull request as you begin working on something. This way, others can see that you're working on it, which avoids duplicate effort, and others can give feedback sooner rather than later if they notice a problem in the direction things are going. Be sure to include "WIP" in the title of the PR as long as it's not ready for review!
- Make sure to create a branch on the roc repository for your changes. We do not allow CI to be run on forks for security. - Make sure to create a branch on the roc repository for your changes. We do not allow CI to be run on forks for security.
@ -31,8 +33,6 @@ Earthly may temporarily use a lot of disk space, up to 90 GB. This disk space is
git config --global commit.gpgsign true git config --global commit.gpgsign true
``` ```
- You find good first issues [here](https://github.com/rtfeldman/roc/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
## Can we do better? ## Can we do better?
Feel free to open an issue if you think this document can be improved or is unclear in any way. Feel free to open an issue if you think this document can be improved or is unclear in any way.

View file

@ -32,6 +32,12 @@ For NQueens, input 10 in the terminal and press enter.
**Tip:** when programming in roc, we recommend to execute `./roc check myproject/Foo.roc` before `./roc myproject/Foo.roc` or `./roc build myproject/Foo.roc`. `./roc check` can produce clear error messages in cases where building/running may panic. **Tip:** when programming in roc, we recommend to execute `./roc check myproject/Foo.roc` before `./roc myproject/Foo.roc` or `./roc build myproject/Foo.roc`. `./roc check` can produce clear error messages in cases where building/running may panic.
## Sponsor
We are very grateful for our sponsor [NoRedInk](https://www.noredink.com/).
<img src="https://www.noredink.com/assets/logo-red-black-f6989d7567cf90b349409137595e99c52d036d755b4403d25528e0fd83a3b084.svg" height="60" alt="NoRedInk logo"/>
## Applications and Platforms ## Applications and Platforms
Applications are often built on a *framework.* Typically, both application and framework are written in the same language. Applications are often built on a *framework.* Typically, both application and framework are written in the same language.

View file

@ -1546,14 +1546,14 @@ an open record or a closed record:
```coffee ```coffee
# Closed record # Closed record
fullName : { firstName : Str, lastName : Str } -> Str fullName : { firstName : Str, lastName : Str } -> Str
fullName = \user - > fullName = \user ->
"\(user.firstName) \(user.lastName)" "\(user.firstName) \(user.lastName)"
``` ```
```coffee ```coffee
# Open record (because of the `*`) # Open record (because of the `*`)
fullName : { firstName : Str, lastName : Str }* -> Str fullName : { firstName : Str, lastName : Str }* -> Str
fullName = \user - > fullName = \user ->
"\(user.firstName) \(user.lastName)" "\(user.firstName) \(user.lastName)"
``` ```

View file

@ -21,7 +21,6 @@ pub fn load_module(src_file: &Path) -> LoadedModule {
}), }),
subs_by_module, subs_by_module,
TargetInfo::default_x86_64(), TargetInfo::default_x86_64(),
roc_can::builtins::builtin_defs_map,
); );
match loaded { match loaded {

View file

@ -4,7 +4,6 @@ use roc_build::{
program, program,
}; };
use roc_builtins::bitcode; use roc_builtins::bitcode;
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_load::file::LoadingProblem; use roc_load::file::LoadingProblem;
use roc_mono::ir::OptLevel; use roc_mono::ir::OptLevel;
@ -74,7 +73,6 @@ pub fn build_file<'a>(
src_dir.as_path(), src_dir.as_path(),
subs_by_module, subs_by_module,
target_info, target_info,
builtin_defs_map,
)?; )?;
use target_lexicon::Architecture; use target_lexicon::Architecture;
@ -309,7 +307,9 @@ fn spawn_rebuild_thread(
) -> std::thread::JoinHandle<u128> { ) -> std::thread::JoinHandle<u128> {
let thread_local_target = target.clone(); let thread_local_target = target.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
print!("🔨 Rebuilding host... "); if !precompiled {
print!("🔨 Rebuilding host... ");
}
let rebuild_host_start = SystemTime::now(); let rebuild_host_start = SystemTime::now();
if !precompiled { if !precompiled {
@ -340,7 +340,9 @@ fn spawn_rebuild_thread(
} }
let rebuild_host_end = rebuild_host_start.elapsed().unwrap(); let rebuild_host_end = rebuild_host_start.elapsed().unwrap();
println!("Done!"); if !precompiled {
println!("Done!");
}
rebuild_host_end.as_millis() rebuild_host_end.as_millis()
}) })
@ -372,7 +374,6 @@ pub fn check_file(
src_dir.as_path(), src_dir.as_path(),
subs_by_module, subs_by_module,
target_info, target_info,
builtin_defs_map,
)?; )?;
let buf = &mut String::with_capacity(1024); let buf = &mut String::with_capacity(1024);

View file

@ -1,5 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::FormatMode;
use bumpalo::collections::Vec; 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};
@ -24,7 +25,7 @@ use roc_parse::{
}; };
use roc_region::all::{Loc, Region}; use roc_region::all::{Loc, Region};
pub fn format(files: std::vec::Vec<PathBuf>) { pub fn format(files: std::vec::Vec<PathBuf>, mode: FormatMode) -> Result<(), String> {
for file in files { for file in files {
let arena = Bump::new(); let arena = Bump::new();
@ -99,9 +100,22 @@ pub fn format(files: std::vec::Vec<PathBuf>) {
unstable_2_file.display()); unstable_2_file.display());
} }
// If all the checks above passed, actually write out the new file. match mode {
std::fs::write(&file, buf.as_str()).unwrap(); FormatMode::CheckOnly => {
// If we notice that this file needs to be formatted, return early
if buf.as_str() != src {
return Err("One or more files need to be reformatted.".to_string());
}
}
FormatMode::Format => {
// If all the checks above passed, actually write out the new file.
std::fs::write(&file, buf.as_str()).unwrap();
}
}
} }
Ok(())
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]

View file

@ -37,6 +37,7 @@ pub const FLAG_TIME: &str = "time";
pub const FLAG_LINK: &str = "roc-linker"; pub const FLAG_LINK: &str = "roc-linker";
pub const FLAG_PRECOMPILED: &str = "precompiled-host"; pub const FLAG_PRECOMPILED: &str = "precompiled-host";
pub const FLAG_VALGRIND: &str = "valgrind"; pub const FLAG_VALGRIND: &str = "valgrind";
pub const FLAG_CHECK: &str = "check";
pub const ROC_FILE: &str = "ROC_FILE"; pub const ROC_FILE: &str = "ROC_FILE";
pub const ROC_DIR: &str = "ROC_DIR"; pub const ROC_DIR: &str = "ROC_DIR";
pub const BACKEND: &str = "BACKEND"; pub const BACKEND: &str = "BACKEND";
@ -122,6 +123,12 @@ pub fn build_app<'a>() -> App<'a> {
.index(1) .index(1)
.multiple_values(true) .multiple_values(true)
.required(false)) .required(false))
.arg(
Arg::new(FLAG_CHECK)
.long(FLAG_CHECK)
.about("Checks that specified files are formatted. If formatting is needed, it will return a non-zero exit code.")
.required(false),
)
) )
.subcommand(App::new(CMD_VERSION) .subcommand(App::new(CMD_VERSION)
.about("Print version information") .about("Print version information")
@ -242,6 +249,11 @@ pub enum BuildConfig {
BuildAndRun { roc_file_arg_index: usize }, BuildAndRun { roc_file_arg_index: usize },
} }
pub enum FormatMode {
Format,
CheckOnly,
}
pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> { pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
use build::build_file; use build::build_file;
use std::str::FromStr; use std::str::FromStr;

View file

@ -1,7 +1,7 @@
use roc_cli::build::check_file; use roc_cli::build::check_file;
use roc_cli::{ use roc_cli::{
build_app, docs, format, BuildConfig, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT, CMD_FORMAT, build_app, docs, format, BuildConfig, FormatMode, CMD_BUILD, CMD_CHECK, CMD_DOCS, CMD_EDIT,
CMD_REPL, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_TIME, ROC_FILE, CMD_FORMAT, CMD_REPL, CMD_VERSION, DIRECTORY_OR_FILES, FLAG_CHECK, FLAG_TIME, ROC_FILE,
}; };
use roc_load::file::LoadingProblem; use roc_load::file::LoadingProblem;
use std::fs::{self, FileType}; use std::fs::{self, FileType};
@ -150,9 +150,20 @@ fn main() -> io::Result<()> {
roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?; roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?;
} }
format(roc_files); let format_mode = match matches.is_present(FLAG_CHECK) {
true => FormatMode::CheckOnly,
false => FormatMode::Format,
};
Ok(0) let format_exit_code = match format(roc_files, format_mode) {
Ok(_) => 0,
Err(message) => {
eprintln!("{}", message);
1
}
};
Ok(format_exit_code)
} }
Some((CMD_VERSION, _)) => { Some((CMD_VERSION, _)) => {
println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n")); println!("roc {}", concatcp!(include_str!("../../version.txt"), "\n"));

58
cli_utils/Cargo.lock generated
View file

@ -964,12 +964,6 @@ dependencies = [
"toml", "toml",
] ]
[[package]]
name = "fixedbitset"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.22" version = "1.0.22"
@ -2047,14 +2041,11 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [ dependencies = [
"backtrace",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"instant", "instant",
"libc", "libc",
"petgraph",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"thread-id",
"winapi", "winapi",
] ]
@ -2107,16 +2098,6 @@ dependencies = [
"sha-1", "sha-1",
] ]
[[package]]
name = "petgraph"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]] [[package]]
name = "phf" name = "phf"
version = "0.9.0" version = "0.9.0"
@ -2790,6 +2771,7 @@ dependencies = [
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"ven_pretty", "ven_pretty",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -2799,6 +2781,7 @@ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
"roc_collections", "roc_collections",
"roc_error_macros",
"roc_ident", "roc_ident",
"roc_region", "roc_region",
"snafu", "snafu",
@ -2815,6 +2798,7 @@ dependencies = [
"roc_builtins", "roc_builtins",
"roc_can", "roc_can",
"roc_collections", "roc_collections",
"roc_error_macros",
"roc_module", "roc_module",
"roc_problem", "roc_problem",
"roc_region", "roc_region",
@ -2865,6 +2849,7 @@ dependencies = [
"inkwell 0.1.0", "inkwell 0.1.0",
"libloading 0.7.1", "libloading 0.7.1",
"roc_build", "roc_build",
"roc_builtins",
"roc_collections", "roc_collections",
"roc_gen_llvm", "roc_gen_llvm",
"roc_load", "roc_load",
@ -2895,6 +2880,7 @@ dependencies = [
"roc_reporting", "roc_reporting",
"roc_target", "roc_target",
"roc_types", "roc_types",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -2927,6 +2913,7 @@ dependencies = [
"roc_region", "roc_region",
"roc_types", "roc_types",
"roc_unify", "roc_unify",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -2946,6 +2933,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"roc_collections", "roc_collections",
"roc_error_macros",
"roc_module", "roc_module",
"roc_region", "roc_region",
"static_assertions", "static_assertions",
@ -2956,6 +2944,7 @@ dependencies = [
name = "roc_unify" name = "roc_unify"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bitflags",
"roc_collections", "roc_collections",
"roc_module", "roc_module",
"roc_types", "roc_types",
@ -3402,17 +3391,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "thread-id"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fdfe0627923f7411a43ec9ec9c39c3a9b4151be313e0922042581fb6c9b717f"
dependencies = [
"libc",
"redox_syscall",
"winapi",
]
[[package]] [[package]]
name = "threadpool" name = "threadpool"
version = "1.8.1" version = "1.8.1"
@ -3583,9 +3561,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.78" version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -3593,9 +3571,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.78" version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"lazy_static", "lazy_static",
@ -3620,9 +3598,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.78" version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -3630,9 +3608,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.78" version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3643,9 +3621,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.78" version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
[[package]] [[package]]
name = "wayland-client" name = "wayland-client"

View file

@ -210,6 +210,7 @@ pub fn listMap(
} }
} }
// List.mapWithIndex : List before, (before, Nat -> after) -> List after
pub fn listMapWithIndex( pub fn listMapWithIndex(
list: RocList, list: RocList,
caller: Caller2, caller: Caller2,
@ -231,7 +232,8 @@ pub fn listMapWithIndex(
} }
while (i < size) : (i += 1) { while (i < size) : (i += 1) {
caller(data, @ptrCast(?[*]u8, &i), source_ptr + (i * old_element_width), target_ptr + (i * new_element_width)); // before, Nat -> after
caller(data, source_ptr + (i * old_element_width), @ptrCast(?[*]u8, &i), target_ptr + (i * new_element_width));
} }
return output; return output;

View file

@ -207,7 +207,7 @@ empty : List *
## Returns a list with the given length, where every element is the given value. ## Returns a list with the given length, where every element is the given value.
## ##
## ##
repeat : Nat, elem -> List elem repeat : elem, Nat -> List elem
## Returns a list of all the integers between one and another, ## Returns a list of all the integers between one and another,
## including both of the given numbers. ## including both of the given numbers.
@ -279,7 +279,7 @@ map4 : List a, List b, List c, List d, (a, b, c, d -> e) -> List e
## This works like [List.map], except it also passes the index ## This works like [List.map], except it also passes the index
## of the element to the conversion function. ## of the element to the conversion function.
mapWithIndex : List before, (Nat, before -> after) -> List after mapWithIndex : List before, (before, Nat -> after) -> List after
## This works like [List.map], except at any time you can return `Err` to ## This works like [List.map], except at any time you can return `Err` to
## cancel the entire operation immediately, and return that #Err. ## cancel the entire operation immediately, and return that #Err.

View file

@ -1078,14 +1078,14 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(list_type(flex(TVAR2))), Box::new(list_type(flex(TVAR2))),
); );
// mapWithIndex : List before, (Nat, before -> after) -> List after // mapWithIndex : List before, (before, Nat -> after) -> List after
{ {
let_tvars! { cvar, before, after}; let_tvars! { cvar, before, after};
add_top_level_function_type!( add_top_level_function_type!(
Symbol::LIST_MAP_WITH_INDEX, Symbol::LIST_MAP_WITH_INDEX,
vec![ vec![
list_type(flex(before)), list_type(flex(before)),
closure(vec![nat_type(), flex(before)], cvar, Box::new(flex(after))), closure(vec![flex(before), nat_type()], cvar, Box::new(flex(after))),
], ],
Box::new(list_type(flex(after))), Box::new(list_type(flex(after))),
) )
@ -1264,10 +1264,10 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(list_type(flex(TVAR1))) Box::new(list_type(flex(TVAR1)))
); );
// repeat : Nat, elem -> List elem // repeat : elem, Nat -> List elem
add_top_level_function_type!( add_top_level_function_type!(
Symbol::LIST_REPEAT, Symbol::LIST_REPEAT,
vec![nat_type(), flex(TVAR1)], vec![flex(TVAR1), nat_type()],
Box::new(list_type(flex(TVAR1))), Box::new(list_type(flex(TVAR1))),
); );

View file

@ -3300,7 +3300,7 @@ fn list_map(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListMap, var_store) lowlevel_2(symbol, LowLevel::ListMap, var_store)
} }
/// List.mapWithIndex : List before, (Nat, before -> after) -> List after /// List.mapWithIndex : List before, (before, Nat -> after) -> List after
fn list_map_with_index(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_map_with_index(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_2(symbol, LowLevel::ListMapWithIndex, var_store) lowlevel_2(symbol, LowLevel::ListMapWithIndex, var_store)
} }

View file

@ -9,6 +9,7 @@ use crate::expr::{
}; };
use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern}; use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern};
use crate::procedure::References; use crate::procedure::References;
use crate::scope::create_alias;
use crate::scope::Scope; use crate::scope::Scope;
use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap}; use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap};
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
@ -22,7 +23,7 @@ use roc_types::subs::{VarStore, Variable};
use roc_types::types::{Alias, Type}; use roc_types::types::{Alias, Type};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use ven_graph::{strongly_connected_components, topological_sort, topological_sort_into_groups}; use ven_graph::{strongly_connected_components, topological_sort};
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Def { pub struct Def {
@ -265,8 +266,7 @@ pub fn canonicalize_defs<'a>(
let (name, vars, ann) = alias_defs.remove(&alias_name).unwrap(); let (name, vars, ann) = alias_defs.remove(&alias_name).unwrap();
let symbol = name.value; let symbol = name.value;
let mut can_ann = let can_ann = canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
// Record all the annotation's references in output.references.lookups // Record all the annotation's references in output.references.lookups
for symbol in can_ann.references { for symbol in can_ann.references {
@ -303,43 +303,21 @@ pub fn canonicalize_defs<'a>(
continue; continue;
} }
let mut is_nested_datatype = false; let alias = create_alias(symbol, name.region, can_vars.clone(), can_ann.typ.clone());
if can_ann.typ.contains_symbol(symbol) {
let alias_args = can_vars
.iter()
.map(|l| (l.value.0.clone(), Type::Variable(l.value.1)))
.collect::<Vec<_>>();
let alias_region =
Region::across_all([name.region].iter().chain(vars.iter().map(|l| &l.region)));
let made_recursive = make_tag_union_recursive(
env,
Loc::at(alias_region, (symbol, &alias_args)),
name.region,
vec![],
&mut can_ann.typ,
var_store,
// Don't report any errors yet. We'll take care of self and mutual
// recursion errors after the sorted introductions are complete.
&mut false,
);
is_nested_datatype = made_recursive.is_err();
}
if is_nested_datatype {
// Bail out
continue;
}
scope.add_alias(symbol, name.region, can_vars.clone(), can_ann.typ.clone());
let alias = scope.lookup_alias(symbol).expect("alias is added to scope");
aliases.insert(symbol, alias.clone()); aliases.insert(symbol, alias.clone());
} }
// Now that we know the alias dependency graph, we can try to insert recursion variables // Now that we know the alias dependency graph, we can try to insert recursion variables
// where aliases are recursive tag unions, or detect illegal recursions. // where aliases are recursive tag unions, or detect illegal recursions.
correct_mutual_recursive_type_alias(env, &mut aliases, var_store); let mut aliases = correct_mutual_recursive_type_alias(env, &aliases, var_store);
for (symbol, alias) in aliases.iter() {
scope.add_alias(
*symbol,
alias.region,
alias.type_variables.clone(),
alias.typ.clone(),
);
}
// Now that we have the scope completely assembled, and shadowing resolved, // Now that we have the scope completely assembled, and shadowing resolved,
// we're ready to canonicalize any body exprs. // we're ready to canonicalize any body exprs.
@ -1564,17 +1542,17 @@ fn pending_typed_body<'a>(
/// Make aliases recursive /// Make aliases recursive
fn correct_mutual_recursive_type_alias<'a>( fn correct_mutual_recursive_type_alias<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
aliases: &mut SendMap<Symbol, Alias>, original_aliases: &SendMap<Symbol, Alias>,
var_store: &mut VarStore, var_store: &mut VarStore,
) { ) -> SendMap<Symbol, Alias> {
let mut symbols_introduced = ImSet::default(); let mut symbols_introduced = ImSet::default();
for (key, _) in aliases.iter() { for (key, _) in original_aliases.iter() {
symbols_introduced.insert(*key); symbols_introduced.insert(*key);
} }
let all_successors_with_self = |symbol: &Symbol| -> ImSet<Symbol> { let all_successors_with_self = |symbol: &Symbol| -> ImSet<Symbol> {
match aliases.get(symbol) { match original_aliases.get(symbol) {
Some(alias) => { Some(alias) => {
let mut loc_succ = alias.typ.symbols(); let mut loc_succ = alias.typ.symbols();
// remove anything that is not defined in the current block // remove anything that is not defined in the current block
@ -1586,80 +1564,120 @@ fn correct_mutual_recursive_type_alias<'a>(
} }
}; };
let all_successors_without_self = |symbol: &Symbol| -> ImSet<Symbol> {
match aliases.get(symbol) {
Some(alias) => {
let mut loc_succ = alias.typ.symbols();
// remove anything that is not defined in the current block
loc_succ.retain(|key| symbols_introduced.contains(key));
loc_succ.remove(symbol);
loc_succ
}
None => ImSet::default(),
}
};
let originals = aliases.clone();
// TODO investigate should this be in a loop? // TODO investigate should this be in a loop?
let defined_symbols: Vec<Symbol> = aliases.keys().copied().collect(); let defined_symbols: Vec<Symbol> = original_aliases.keys().copied().collect();
// split into self-recursive and mutually recursive let cycles = strongly_connected_components(&defined_symbols, all_successors_with_self);
match topological_sort_into_groups(&defined_symbols, all_successors_with_self) { let mut solved_aliases = SendMap::default();
Ok(_) => {
// no mutual recursion in any alias
}
Err((_, mutually_recursive_symbols)) => {
for cycle in strongly_connected_components(
&mutually_recursive_symbols,
all_successors_without_self,
) {
// make sure we report only one error for the cycle, not an error for every
// alias in the cycle.
let mut can_still_report_error = true;
// TODO use itertools to be more efficient here for cycle in cycles {
for rec in &cycle { debug_assert!(!cycle.is_empty());
let mut to_instantiate = ImMap::default();
let mut others = Vec::with_capacity(cycle.len() - 1);
for other in &cycle {
if rec != other {
others.push(*other);
if let Some(alias) = originals.get(other) {
to_instantiate.insert(*other, alias.clone());
}
}
}
if let Some(alias) = aliases.get_mut(rec) { let mut pending_aliases: SendMap<_, _> = cycle
alias.typ.instantiate_aliases( .iter()
alias.region, .map(|&sym| (sym, original_aliases.get(&sym).unwrap().clone()))
&to_instantiate, .collect();
var_store,
&mut ImSet::default(),
);
let alias_args = &alias // Make sure we report only one error for the cycle, not an error for every
.type_variables // alias in the cycle.
.iter() let mut can_still_report_error = true;
.map(|l| (l.value.0.clone(), Type::Variable(l.value.1)))
.collect::<Vec<_>>();
let _made_recursive = make_tag_union_recursive( for &rec in cycle.iter() {
env, // First, we need to instantiate the alias with any symbols in the currrent module it
Loc::at(alias.header_region(), (*rec, alias_args)), // depends on.
alias.region, // We only need to worry about symbols in this SCC or any prior one, since the SCCs
others, // were sorted topologically, and we've already instantiated aliases coming from other
&mut alias.typ, // modules.
var_store, let mut to_instantiate: ImMap<_, _> = solved_aliases.clone().into_iter().collect();
&mut can_still_report_error, let mut others_in_scc = Vec::with_capacity(cycle.len() - 1);
); for &other in cycle.iter() {
if rec != other {
others_in_scc.push(other);
if let Some(alias) = original_aliases.get(&other) {
to_instantiate.insert(other, alias.clone());
} }
} }
} }
let alias = pending_aliases.get_mut(&rec).unwrap();
alias.typ.instantiate_aliases(
alias.region,
&to_instantiate,
var_store,
&mut ImSet::default(),
);
// Now mark the alias recursive, if it needs to be.
let is_self_recursive = alias.typ.contains_symbol(rec);
let is_mutually_recursive = cycle.len() > 1;
if is_self_recursive || is_mutually_recursive {
let _made_recursive = make_tag_union_of_alias_recursive(
env,
rec,
alias,
vec![],
var_store,
&mut can_still_report_error,
);
}
} }
// The cycle we just instantiated and marked recursive may still be an illegal cycle, if
// all the types in the cycle are narrow newtypes. We can't figure this out until now,
// because we need all the types to be deeply instantiated.
let all_are_narrow = cycle.iter().all(|sym| {
let typ = &pending_aliases.get(sym).unwrap().typ;
matches!(typ, Type::RecursiveTagUnion(..)) && typ.is_narrow()
});
if all_are_narrow {
// This cycle is illegal!
let mut rest = cycle;
let alias_name = rest.pop().unwrap();
let alias = pending_aliases.get_mut(&alias_name).unwrap();
mark_cyclic_alias(
env,
&mut alias.typ,
alias_name,
alias.region,
rest,
can_still_report_error,
)
}
// Now, promote all resolved aliases in this cycle as solved.
solved_aliases.extend(pending_aliases);
} }
solved_aliases
}
fn make_tag_union_of_alias_recursive<'a>(
env: &mut Env<'a>,
alias_name: Symbol,
alias: &mut Alias,
others: Vec<Symbol>,
var_store: &mut VarStore,
can_report_error: &mut bool,
) -> Result<(), ()> {
let alias_args = alias
.type_variables
.iter()
.map(|l| (l.value.0.clone(), Type::Variable(l.value.1)))
.collect::<Vec<_>>();
make_tag_union_recursive_help(
env,
Loc::at(alias.header_region(), (alias_name, &alias_args)),
alias.region,
others,
&mut alias.typ,
var_store,
can_report_error,
)
} }
/// Attempt to make a tag union recursive at the position of `recursive_alias`; for example, /// Attempt to make a tag union recursive at the position of `recursive_alias`; for example,
@ -1683,7 +1701,7 @@ fn correct_mutual_recursive_type_alias<'a>(
/// ``` /// ```
/// ///
/// When `Err` is returned, a problem will be added to `env`. /// When `Err` is returned, a problem will be added to `env`.
fn make_tag_union_recursive<'a>( fn make_tag_union_recursive_help<'a>(
env: &mut Env<'a>, env: &mut Env<'a>,
recursive_alias: Loc<(Symbol, &[(Lowercase, Type)])>, recursive_alias: Loc<(Symbol, &[(Lowercase, Type)])>,
region: Region, region: Region,
@ -1724,7 +1742,7 @@ fn make_tag_union_recursive<'a>(
actual, actual,
type_arguments, type_arguments,
.. ..
} => make_tag_union_recursive( } => make_tag_union_recursive_help(
env, env,
Loc::at_zero((symbol, type_arguments)), Loc::at_zero((symbol, type_arguments)),
region, region,
@ -1734,17 +1752,27 @@ fn make_tag_union_recursive<'a>(
can_report_error, can_report_error,
), ),
_ => { _ => {
let problem = roc_types::types::Problem::CyclicAlias(symbol, region, others.clone()); mark_cyclic_alias(env, typ, symbol, region, others, *can_report_error);
*typ = Type::Erroneous(problem); *can_report_error = false;
// ensure cyclic error is only reported for one element of the cycle
if *can_report_error {
*can_report_error = false;
let problem = Problem::CyclicAlias(symbol, region, others);
env.problems.push(problem);
}
Ok(()) Ok(())
} }
} }
} }
fn mark_cyclic_alias<'a>(
env: &mut Env<'a>,
typ: &mut Type,
symbol: Symbol,
region: Region,
others: Vec<Symbol>,
report: bool,
) {
let problem = roc_types::types::Problem::CyclicAlias(symbol, region, others.clone());
*typ = Type::Erroneous(problem);
if report {
let problem = Problem::CyclicAlias(symbol, region, others);
env.problems.push(problem);
}
}

View file

@ -67,7 +67,7 @@ fn validate_generate_with<'a>(
// TODO trim these down // TODO trim these down
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn canonicalize_module_defs<'a, F>( pub fn canonicalize_module_defs<'a>(
arena: &Bump, arena: &Bump,
loc_defs: &'a [Loc<ast::Def<'a>>], loc_defs: &'a [Loc<ast::Def<'a>>],
header_for: &roc_parse::header::HeaderFor, header_for: &roc_parse::header::HeaderFor,
@ -79,11 +79,7 @@ pub fn canonicalize_module_defs<'a, F>(
exposed_imports: MutMap<Ident, (Symbol, Region)>, exposed_imports: MutMap<Ident, (Symbol, Region)>,
exposed_symbols: &MutSet<Symbol>, exposed_symbols: &MutSet<Symbol>,
var_store: &mut VarStore, var_store: &mut VarStore,
look_up_builtin: F, ) -> Result<ModuleOutput, RuntimeError> {
) -> Result<ModuleOutput, RuntimeError>
where
F: Fn(Symbol, &mut VarStore) -> Option<Def> + 'static + Send + Copy,
{
let mut can_exposed_imports = MutMap::default(); let mut can_exposed_imports = MutMap::default();
let mut scope = Scope::new(home, var_store); let mut scope = Scope::new(home, var_store);
let mut env = Env::new(home, dep_idents, module_ids, exposed_ident_ids); let mut env = Env::new(home, dep_idents, module_ids, exposed_ident_ids);
@ -482,7 +478,7 @@ where
for symbol in references.iter() { for symbol in references.iter() {
if symbol.is_builtin() { if symbol.is_builtin() {
// this can fail when the symbol is for builtin types, or has no implementation yet // this can fail when the symbol is for builtin types, or has no implementation yet
if let Some(def) = look_up_builtin(*symbol, var_store) { if let Some(def) = crate::builtins::builtin_defs_map(*symbol, var_store) {
declarations.push(Declaration::Builtin(def)); declarations.push(Declaration::Builtin(def));
} }
} }

View file

@ -181,42 +181,7 @@ impl Scope {
vars: Vec<Loc<(Lowercase, Variable)>>, vars: Vec<Loc<(Lowercase, Variable)>>,
typ: Type, typ: Type,
) { ) {
let roc_types::types::VariableDetail { let alias = create_alias(name, region, vars, typ);
type_variables,
lambda_set_variables,
recursion_variables,
} = typ.variables_detail();
debug_assert!({
let mut hidden = type_variables;
for loc_var in vars.iter() {
hidden.remove(&loc_var.value.1);
}
if !hidden.is_empty() {
panic!(
"Found unbound type variables {:?} \n in type alias {:?} {:?} : {:?}",
hidden, name, &vars, &typ
)
}
true
});
let lambda_set_variables: Vec<_> = lambda_set_variables
.into_iter()
.map(|v| roc_types::types::LambdaSet(Type::Variable(v)))
.collect();
let alias = Alias {
region,
type_variables: vars,
lambda_set_variables,
recursion_variables,
typ,
};
self.aliases.insert(name, alias); self.aliases.insert(name, alias);
} }
@ -224,3 +189,46 @@ impl Scope {
self.aliases.contains_key(&name) self.aliases.contains_key(&name)
} }
} }
pub fn create_alias(
name: Symbol,
region: Region,
vars: Vec<Loc<(Lowercase, Variable)>>,
typ: Type,
) -> Alias {
let roc_types::types::VariableDetail {
type_variables,
lambda_set_variables,
recursion_variables,
} = typ.variables_detail();
debug_assert!({
let mut hidden = type_variables;
for loc_var in vars.iter() {
hidden.remove(&loc_var.value.1);
}
if !hidden.is_empty() {
panic!(
"Found unbound type variables {:?} \n in type alias {:?} {:?} : {:?}",
hidden, name, &vars, &typ
)
}
true
});
let lambda_set_variables: Vec<_> = lambda_set_variables
.into_iter()
.map(|v| roc_types::types::LambdaSet(Type::Variable(v)))
.collect();
Alias {
region,
type_variables: vars,
lambda_set_variables,
recursion_variables,
typ,
}
}

View file

@ -5014,7 +5014,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
} }
} }
ListMapWithIndex { xs } => { ListMapWithIndex { xs } => {
// List.mapWithIndex : List before, (Nat, before -> after) -> List after // List.mapWithIndex : List before, (before, Nat -> after) -> List after
let (list, list_layout) = load_symbol_and_layout(scope, xs); let (list, list_layout) = load_symbol_and_layout(scope, xs);
let (function, closure, closure_layout) = function_details!(); let (function, closure, closure_layout) = function_details!();
@ -5024,7 +5024,7 @@ fn run_higher_order_low_level<'a, 'ctx, 'env>(
Layout::Builtin(Builtin::List(element_layout)), Layout::Builtin(Builtin::List(element_layout)),
Layout::Builtin(Builtin::List(result_layout)), Layout::Builtin(Builtin::List(result_layout)),
) => { ) => {
let argument_layouts = &[Layout::usize(env.target_info), **element_layout]; let argument_layouts = &[**element_layout, Layout::usize(env.target_info)];
let roc_function_call = roc_function_call( let roc_function_call = roc_function_call(
env, env,
@ -5484,13 +5484,13 @@ fn run_low_level<'a, 'ctx, 'env>(
list_single(env, arg, arg_layout) list_single(env, arg, arg_layout)
} }
ListRepeat => { ListRepeat => {
// List.repeat : Int, elem -> List elem // List.repeat : elem, Nat -> List elem
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
let list_len = load_symbol(scope, &args[0]).into_int_value(); let (elem, elem_layout) = load_symbol_and_layout(scope, &args[0]);
let (elem, elem_layout) = load_symbol_and_layout(scope, &args[1]); let list_len = load_symbol(scope, &args[1]).into_int_value();
list_repeat(env, layout_ids, list_len, elem, elem_layout) list_repeat(env, layout_ids, elem, elem_layout, list_len)
} }
ListReverse => { ListReverse => {
// List.reverse : List elem -> List elem // List.reverse : List elem -> List elem
@ -5692,7 +5692,7 @@ fn run_low_level<'a, 'ctx, 'env>(
match arg_builtin { match arg_builtin {
Int(int_width) => { Int(int_width) => {
let int_type = convert::int_type_from_int_width(env, *int_width); let int_type = convert::int_type_from_int_width(env, *int_width);
build_int_unary_op(env, arg.into_int_value(), int_type, op) build_int_unary_op(env, arg.into_int_value(), int_type, op, layout)
} }
Float(float_width) => build_float_unary_op( Float(float_width) => build_float_unary_op(
env, env,
@ -6924,6 +6924,7 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
arg: IntValue<'ctx>, arg: IntValue<'ctx>,
int_type: IntType<'ctx>, int_type: IntType<'ctx>,
op: LowLevel, op: LowLevel,
return_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
use roc_module::low_level::LowLevel::*; use roc_module::low_level::LowLevel::*;
@ -6939,12 +6940,19 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
int_abs_raise_on_overflow(env, arg, int_type) int_abs_raise_on_overflow(env, arg, int_type)
} }
NumToFloat => { NumToFloat => {
// TODO: Handle different sized numbers
// This is an Int, so we need to convert it. // This is an Int, so we need to convert it.
let target_float_type = match return_layout {
Layout::Builtin(Builtin::Float(float_width)) => {
convert::float_type_from_float_width(env, *float_width)
}
_ => internal_error!("There can only be floats here!"),
};
bd.build_cast( bd.build_cast(
InstructionOpcode::SIToFP, InstructionOpcode::SIToFP,
arg, arg,
env.context.f64_type(), target_float_type,
"i64_to_f64", "i64_to_f64",
) )
} }

View file

@ -119,13 +119,13 @@ pub fn list_single<'a, 'ctx, 'env>(
) )
} }
/// List.repeat : Int, elem -> List elem /// List.repeat : elem, Nat -> List elem
pub fn list_repeat<'a, 'ctx, 'env>( pub fn list_repeat<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>, layout_ids: &mut LayoutIds<'a>,
list_len: IntValue<'ctx>,
element: BasicValueEnum<'ctx>, element: BasicValueEnum<'ctx>,
element_layout: &Layout<'a>, element_layout: &Layout<'a>,
list_len: IntValue<'ctx>,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
let inc_element_fn = build_inc_n_wrapper(env, layout_ids, element_layout); let inc_element_fn = build_inc_n_wrapper(env, layout_ids, element_layout);
@ -687,7 +687,7 @@ pub fn list_sort_with<'a, 'ctx, 'env>(
) )
} }
/// List.mapWithIndex : List before, (Nat, before -> after) -> List after /// List.mapWithIndex : List before, (before, Nat -> after) -> List after
pub fn list_map_with_index<'a, 'ctx, 'env>( pub fn list_map_with_index<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
roc_function_call: RocFunctionCall<'ctx>, roc_function_call: RocFunctionCall<'ctx>,

View file

@ -311,7 +311,7 @@ impl Clone for IdentStr {
impl Drop for IdentStr { impl Drop for IdentStr {
fn drop(&mut self) { fn drop(&mut self) {
if !self.is_small_str() { if !self.is_empty() && !self.is_small_str() {
unsafe { unsafe {
let align = mem::align_of::<u8>(); let align = mem::align_of::<u8>();
let layout = Layout::from_size_align_unchecked(self.length, align); let layout = Layout::from_size_align_unchecked(self.length, align);

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
pub mod docs; pub mod docs;
pub mod file; pub mod file;
mod work;
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
mod wasm_system_time; mod wasm_system_time;

View file

@ -11,13 +11,13 @@ Instead we use these dummy implementations, which should just disappear at compi
pub struct SystemTime; pub struct SystemTime;
impl SystemTime { impl SystemTime {
fn now() -> Self { pub fn now() -> Self {
SystemTime SystemTime
} }
fn duration_since(&self, _: SystemTime) -> Result<Duration, String> { pub fn duration_since(&self, _: SystemTime) -> Result<Duration, String> {
Ok(Duration) Ok(Duration)
} }
fn elapsed(&self) -> Result<Duration, String> { pub fn elapsed(&self) -> Result<Duration, String> {
Ok(Duration) Ok(Duration)
} }
} }
@ -26,7 +26,7 @@ impl SystemTime {
pub struct Duration; pub struct Duration;
impl Duration { impl Duration {
fn checked_sub(&self, _: Duration) -> Option<Duration> { pub fn checked_sub(&self, _: Duration) -> Option<Duration> {
Some(Duration) Some(Duration)
} }
} }

290
compiler/load/src/work.rs Normal file
View file

@ -0,0 +1,290 @@
use roc_collections::all::{MutMap, MutSet};
use roc_module::symbol::{ModuleId, PackageQualified};
use std::collections::hash_map::Entry;
/// NOTE the order of definition of the phases is used by the ord instance
/// make sure they are ordered from first to last!
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
pub enum Phase {
LoadHeader,
Parse,
CanonicalizeAndConstrain,
SolveTypes,
FindSpecializations,
MakeSpecializations,
}
/// NOTE keep up to date manually, from ParseAndGenerateConstraints to the highest phase we support
const PHASES: [Phase; 6] = [
Phase::LoadHeader,
Phase::Parse,
Phase::CanonicalizeAndConstrain,
Phase::SolveTypes,
Phase::FindSpecializations,
Phase::MakeSpecializations,
];
#[derive(Debug)]
enum Status {
NotStarted,
Pending,
Done,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum Job<'a> {
Step(ModuleId, Phase),
ResolveShorthand(&'a str),
}
#[derive(Default, Debug)]
pub struct Dependencies<'a> {
waiting_for: MutMap<Job<'a>, MutSet<Job<'a>>>,
notifies: MutMap<Job<'a>, MutSet<Job<'a>>>,
status: MutMap<Job<'a>, Status>,
}
impl<'a> Dependencies<'a> {
/// Add all the dependencies for a module, return (module, phase) pairs that can make progress
pub fn add_module(
&mut self,
module_id: ModuleId,
dependencies: &MutSet<PackageQualified<'a, ModuleId>>,
goal_phase: Phase,
) -> MutSet<(ModuleId, Phase)> {
use Phase::*;
let mut output = MutSet::default();
for dep in dependencies.iter() {
let has_package_dependency = self.add_package_dependency(dep, Phase::LoadHeader);
let dep = *dep.as_inner();
if !has_package_dependency {
// loading can start immediately on this dependency
output.insert((dep, Phase::LoadHeader));
}
// to parse and generate constraints, the headers of all dependencies must be loaded!
// otherwise, we don't know whether an imported symbol is actually exposed
self.add_dependency_help(module_id, dep, Phase::Parse, Phase::LoadHeader);
// to canonicalize a module, all its dependencies must be canonicalized
self.add_dependency(module_id, dep, Phase::CanonicalizeAndConstrain);
// to typecheck a module, all its dependencies must be type checked already
self.add_dependency(module_id, dep, Phase::SolveTypes);
if goal_phase >= FindSpecializations {
self.add_dependency(module_id, dep, Phase::FindSpecializations);
}
if goal_phase >= MakeSpecializations {
self.add_dependency(dep, module_id, Phase::MakeSpecializations);
}
}
// add dependencies for self
// phase i + 1 of a file always depends on phase i being completed
{
let mut i = 0;
while PHASES[i] < goal_phase {
self.add_dependency_help(module_id, module_id, PHASES[i + 1], PHASES[i]);
i += 1;
}
}
self.add_to_status(module_id, goal_phase);
output
}
fn add_to_status(&mut self, module_id: ModuleId, goal_phase: Phase) {
for phase in PHASES.iter() {
if *phase > goal_phase {
break;
}
if let Entry::Vacant(entry) = self.status.entry(Job::Step(module_id, *phase)) {
entry.insert(Status::NotStarted);
}
}
}
/// Propagate a notification, return (module, phase) pairs that can make progress
pub fn notify(&mut self, module_id: ModuleId, phase: Phase) -> MutSet<(ModuleId, Phase)> {
self.notify_help(Job::Step(module_id, phase))
}
/// Propagate a notification, return (module, phase) pairs that can make progress
pub fn notify_package(&mut self, shorthand: &'a str) -> MutSet<(ModuleId, Phase)> {
self.notify_help(Job::ResolveShorthand(shorthand))
}
fn notify_help(&mut self, key: Job<'a>) -> MutSet<(ModuleId, Phase)> {
self.status.insert(key.clone(), Status::Done);
let mut output = MutSet::default();
if let Some(to_notify) = self.notifies.get(&key) {
for notify_key in to_notify {
let mut is_empty = false;
if let Some(waiting_for_pairs) = self.waiting_for.get_mut(notify_key) {
waiting_for_pairs.remove(&key);
is_empty = waiting_for_pairs.is_empty();
}
if is_empty {
self.waiting_for.remove(notify_key);
if let Job::Step(module, phase) = *notify_key {
output.insert((module, phase));
}
}
}
}
self.notifies.remove(&key);
output
}
fn add_package_dependency(
&mut self,
module: &PackageQualified<'a, ModuleId>,
next_phase: Phase,
) -> bool {
match module {
PackageQualified::Unqualified(_) => {
// no dependency, we can just start loading the file
false
}
PackageQualified::Qualified(shorthand, module_id) => {
let job = Job::ResolveShorthand(shorthand);
let next_step = Job::Step(*module_id, next_phase);
match self.status.get(&job) {
None | Some(Status::NotStarted) | Some(Status::Pending) => {
// this shorthand is not resolved, add a dependency
{
let entry = self
.waiting_for
.entry(next_step.clone())
.or_insert_with(Default::default);
entry.insert(job.clone());
}
{
let entry = self.notifies.entry(job).or_insert_with(Default::default);
entry.insert(next_step);
}
true
}
Some(Status::Done) => {
// shorthand is resolved; no dependency
false
}
}
}
}
}
/// A waits for B, and B will notify A when it completes the phase
fn add_dependency(&mut self, a: ModuleId, b: ModuleId, phase: Phase) {
self.add_dependency_help(a, b, phase, phase);
}
/// phase_a of module a is waiting for phase_b of module_b
fn add_dependency_help(&mut self, a: ModuleId, b: ModuleId, phase_a: Phase, phase_b: Phase) {
// no need to wait if the dependency is already done!
if let Some(Status::Done) = self.status.get(&Job::Step(b, phase_b)) {
return;
}
let key = Job::Step(a, phase_a);
let value = Job::Step(b, phase_b);
match self.waiting_for.get_mut(&key) {
Some(existing) => {
existing.insert(value);
}
None => {
let mut set = MutSet::default();
set.insert(value);
self.waiting_for.insert(key, set);
}
}
let key = Job::Step(b, phase_b);
let value = Job::Step(a, phase_a);
match self.notifies.get_mut(&key) {
Some(existing) => {
existing.insert(value);
}
None => {
let mut set = MutSet::default();
set.insert(value);
self.notifies.insert(key, set);
}
}
}
pub fn solved_all(&self) -> bool {
debug_assert_eq!(self.notifies.is_empty(), self.waiting_for.is_empty());
for status in self.status.values() {
match status {
Status::Done => {
continue;
}
_ => {
return false;
}
}
}
true
}
pub fn prepare_start_phase(&mut self, module_id: ModuleId, phase: Phase) -> PrepareStartPhase {
match self.status.get_mut(&Job::Step(module_id, phase)) {
Some(current @ Status::NotStarted) => {
// start this phase!
*current = Status::Pending;
PrepareStartPhase::Continue
}
Some(Status::Pending) => {
// don't start this task again!
PrepareStartPhase::Done
}
Some(Status::Done) => {
// don't start this task again, but tell those waiting for it they can continue
let new = self.notify(module_id, phase);
PrepareStartPhase::Recurse(new)
}
None => match phase {
Phase::LoadHeader => {
// this is fine, mark header loading as pending
self.status
.insert(Job::Step(module_id, Phase::LoadHeader), Status::Pending);
PrepareStartPhase::Continue
}
_ => unreachable!(
"Pair {:?} is not in dependencies.status, that should never happen!",
(module_id, phase)
),
},
}
}
}
pub enum PrepareStartPhase {
Continue,
Done,
Recurse(MutSet<(ModuleId, Phase)>),
}

View file

@ -16,7 +16,6 @@ mod helpers;
mod test_load { mod test_load {
use crate::helpers::fixtures_dir; use crate::helpers::fixtures_dir;
use bumpalo::Bump; use bumpalo::Bump;
use roc_can::builtins::builtin_defs_map;
use roc_can::def::Declaration::*; use roc_can::def::Declaration::*;
use roc_can::def::Def; use roc_can::def::Def;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
@ -111,7 +110,6 @@ mod test_load {
dir.path(), dir.path(),
exposed_types, exposed_types,
TARGET_INFO, TARGET_INFO,
builtin_defs_map,
) )
}; };
@ -135,7 +133,6 @@ mod test_load {
src_dir.as_path(), src_dir.as_path(),
subs_by_module, subs_by_module,
TARGET_INFO, TARGET_INFO,
builtin_defs_map,
); );
let mut loaded_module = match loaded { let mut loaded_module = match loaded {
Ok(x) => x, Ok(x) => x,
@ -301,7 +298,6 @@ mod test_load {
src_dir.as_path(), src_dir.as_path(),
subs_by_module, subs_by_module,
TARGET_INFO, TARGET_INFO,
builtin_defs_map,
); );
let mut loaded_module = loaded.expect("Test module failed to load"); let mut loaded_module = loaded.expect("Test module failed to load");

View file

@ -809,6 +809,7 @@ fn call_spec(
add_loop(builder, block, state_type, init_state, loop_body) add_loop(builder, block, state_type, init_state, loop_body)
} }
// List.mapWithIndex : List before, (before, Nat -> after) -> List after
ListMapWithIndex { xs } => { ListMapWithIndex { xs } => {
let list = env.symbols[xs]; let list = env.symbols[xs];
@ -818,7 +819,8 @@ fn call_spec(
let element = builder.add_bag_get(block, input_bag)?; let element = builder.add_bag_get(block, input_bag)?;
let index = builder.add_make_tuple(block, &[])?; let index = builder.add_make_tuple(block, &[])?;
let new_element = call_function!(builder, block, [index, element]); // before, Nat -> after
let new_element = call_function!(builder, block, [element, index]);
list_append(builder, block, update_mode_var, state, new_element) list_append(builder, block, update_mode_var, state, new_element)
}; };

View file

@ -624,8 +624,9 @@ impl<'a> BorrowInfState<'a> {
} }
} }
ListMapWithIndex { xs } => { ListMapWithIndex { xs } => {
// own the list if the function wants to own the element // List.mapWithIndex : List before, (before, Nat -> after) -> List after
if !function_ps[1].borrow { // own the list if the function wants to own the element (before, index 0)
if !function_ps[0].borrow {
self.own_var(*xs); self.own_var(*xs);
} }
} }
@ -943,7 +944,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]), StrSplit => arena.alloc_slice_copy(&[borrowed, borrowed]),
StrToNum => arena.alloc_slice_copy(&[borrowed]), StrToNum => arena.alloc_slice_copy(&[borrowed]),
ListSingle => arena.alloc_slice_copy(&[irrelevant]), ListSingle => arena.alloc_slice_copy(&[irrelevant]),
ListRepeat => arena.alloc_slice_copy(&[irrelevant, borrowed]), // List.repeat : elem, Nat -> List.elem
ListRepeat => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListReverse => arena.alloc_slice_copy(&[owned]), ListReverse => arena.alloc_slice_copy(&[owned]),
ListPrepend => arena.alloc_slice_copy(&[owned, owned]), ListPrepend => arena.alloc_slice_copy(&[owned, owned]),
StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]), StrJoinWith => arena.alloc_slice_copy(&[borrowed, borrowed]),

View file

@ -1,4 +1,3 @@
target target
corpus corpus
artifacts artifacts

View file

@ -1,4 +1,3 @@
[package] [package]
name = "roc_parse-fuzz" name = "roc_parse-fuzz"
version = "0.0.0" version = "0.0.0"

View file

@ -1,5 +1,6 @@
use crate::ast::CommentOrNewline; use crate::ast::CommentOrNewline;
use crate::ast::Spaceable; use crate::ast::Spaceable;
use crate::parser::SpaceProblem;
use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*}; use crate::parser::{self, and, backtrackable, BadInputError, Parser, Progress::*};
use crate::state::State; use crate::state::State;
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
@ -10,7 +11,6 @@ use roc_region::all::Position;
pub fn space0_around_ee<'a, P, S, E>( pub fn space0_around_ee<'a, P, S, E>(
parser: P, parser: P,
min_indent: u32, min_indent: u32,
space_problem: fn(BadInputError, Position) -> E,
indent_before_problem: fn(Position) -> E, indent_before_problem: fn(Position) -> E,
indent_after_problem: fn(Position) -> E, indent_after_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E> ) -> impl Parser<'a, Loc<S>, E>
@ -19,15 +19,12 @@ where
S: 'a, S: 'a,
P: Parser<'a, Loc<S>, E>, P: Parser<'a, Loc<S>, E>,
P: 'a, P: 'a,
E: 'a, E: 'a + SpaceProblem,
{ {
parser::map_with_arena( parser::map_with_arena(
and( and(
space0_e(min_indent, space_problem, indent_before_problem), space0_e(min_indent, indent_before_problem),
and( and(parser, space0_e(min_indent, indent_after_problem)),
parser,
space0_e(min_indent, space_problem, indent_after_problem),
),
), ),
spaces_around_help, spaces_around_help,
) )
@ -36,7 +33,6 @@ where
pub fn space0_before_optional_after<'a, P, S, E>( pub fn space0_before_optional_after<'a, P, S, E>(
parser: P, parser: P,
min_indent: u32, min_indent: u32,
space_problem: fn(BadInputError, Position) -> E,
indent_before_problem: fn(Position) -> E, indent_before_problem: fn(Position) -> E,
indent_after_problem: fn(Position) -> E, indent_after_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E> ) -> impl Parser<'a, Loc<S>, E>
@ -45,15 +41,15 @@ where
S: 'a, S: 'a,
P: Parser<'a, Loc<S>, E>, P: Parser<'a, Loc<S>, E>,
P: 'a, P: 'a,
E: 'a, E: 'a + SpaceProblem,
{ {
parser::map_with_arena( parser::map_with_arena(
and( and(
space0_e(min_indent, space_problem, indent_before_problem), space0_e(min_indent, indent_before_problem),
and( and(
parser, parser,
one_of![ one_of![
backtrackable(space0_e(min_indent, space_problem, indent_after_problem)), backtrackable(space0_e(min_indent, indent_after_problem)),
succeed!(&[] as &[_]), succeed!(&[] as &[_]),
], ],
), ),
@ -101,7 +97,6 @@ where
pub fn space0_before_e<'a, P, S, E>( pub fn space0_before_e<'a, P, S, E>(
parser: P, parser: P,
min_indent: u32, min_indent: u32,
space_problem: fn(BadInputError, Position) -> E,
indent_problem: fn(Position) -> E, indent_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E> ) -> impl Parser<'a, Loc<S>, E>
where where
@ -109,10 +104,10 @@ where
S: 'a, S: 'a,
P: Parser<'a, Loc<S>, E>, P: Parser<'a, Loc<S>, E>,
P: 'a, P: 'a,
E: 'a, E: 'a + SpaceProblem,
{ {
parser::map_with_arena( parser::map_with_arena(
and!(space0_e(min_indent, space_problem, indent_problem), parser), and!(space0_e(min_indent, indent_problem), parser),
|arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| { |arena: &'a Bump, (space_list, loc_expr): (&'a [CommentOrNewline<'a>], Loc<S>)| {
if space_list.is_empty() { if space_list.is_empty() {
loc_expr loc_expr
@ -128,7 +123,6 @@ where
pub fn space0_after_e<'a, P, S, E>( pub fn space0_after_e<'a, P, S, E>(
parser: P, parser: P,
min_indent: u32, min_indent: u32,
space_problem: fn(BadInputError, Position) -> E,
indent_problem: fn(Position) -> E, indent_problem: fn(Position) -> E,
) -> impl Parser<'a, Loc<S>, E> ) -> impl Parser<'a, Loc<S>, E>
where where
@ -136,10 +130,10 @@ where
S: 'a, S: 'a,
P: Parser<'a, Loc<S>, E>, P: Parser<'a, Loc<S>, E>,
P: 'a, P: 'a,
E: 'a, E: 'a + SpaceProblem,
{ {
parser::map_with_arena( parser::map_with_arena(
and!(parser, space0_e(min_indent, space_problem, indent_problem)), and!(parser, space0_e(min_indent, indent_problem)),
|arena: &'a Bump, (loc_expr, space_list): (Loc<S>, &'a [CommentOrNewline<'a>])| { |arena: &'a Bump, (loc_expr, space_list): (Loc<S>, &'a [CommentOrNewline<'a>])| {
if space_list.is_empty() { if space_list.is_empty() {
loc_expr loc_expr
@ -170,23 +164,21 @@ where
pub fn space0_e<'a, E>( pub fn space0_e<'a, E>(
min_indent: u32, min_indent: u32,
space_problem: fn(BadInputError, Position) -> E,
indent_problem: fn(Position) -> E, indent_problem: fn(Position) -> E,
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> ) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
where where
E: 'a, E: 'a + SpaceProblem,
{ {
spaces_help_help(min_indent, space_problem, indent_problem) spaces_help_help(min_indent, indent_problem)
} }
#[inline(always)] #[inline(always)]
fn spaces_help_help<'a, E>( fn spaces_help_help<'a, E>(
min_indent: u32, min_indent: u32,
space_problem: fn(BadInputError, Position) -> E,
indent_problem: fn(Position) -> E, indent_problem: fn(Position) -> E,
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E> ) -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
where where
E: 'a, E: 'a + SpaceProblem,
{ {
use SpaceState::*; use SpaceState::*;
@ -195,7 +187,7 @@ where
match eat_spaces(state.clone(), false, comments_and_newlines) { match eat_spaces(state.clone(), false, comments_and_newlines) {
HasTab(state) => Err(( HasTab(state) => Err((
MadeProgress, MadeProgress,
space_problem(BadInputError::HasTab, state.pos()), E::space_problem(BadInputError::HasTab, state.pos()),
state, state,
)), )),
Good { Good {

View file

@ -39,7 +39,6 @@ pub fn test_parse_expr<'a>(
space0_before_e( space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s), move |a, s| parse_loc_expr(min_indent, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentStart, EExpr::IndentStart,
), ),
expr_end() expr_end()
@ -106,7 +105,6 @@ fn loc_expr_in_parens_help_help<'a>(
min_indent, arena, state min_indent, arena, state
)), )),
min_indent, min_indent,
EInParens::Space,
EInParens::IndentOpen, EInParens::IndentOpen,
EInParens::IndentEnd, EInParens::IndentEnd,
), ),
@ -340,7 +338,7 @@ fn parse_expr_operator_chain<'a>(
let initial = state.clone(); let initial = state.clone();
let end = state.pos(); let end = state.pos();
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) { match space0_e(min_indent, EExpr::IndentEnd).parse(arena, state) {
Err((_, _, state)) => Ok((MadeProgress, expr.value, state)), Err((_, _, state)) => Ok((MadeProgress, expr.value, state)),
Ok((_, spaces_before_op, state)) => { Ok((_, spaces_before_op, state)) => {
let expr_state = ExprState { let expr_state = ExprState {
@ -781,7 +779,7 @@ fn parse_defs_end<'a>(
let min_indent = start_column; let min_indent = start_column;
let initial = state.clone(); let initial = state.clone();
let state = match space0_e(min_indent, EExpr::Space, EExpr::IndentStart).parse(arena, state) { let state = match space0_e(min_indent, EExpr::IndentStart).parse(arena, state) {
Err((MadeProgress, _, s)) => { Err((MadeProgress, _, s)) => {
return Err((MadeProgress, EExpr::DefMissingFinalExpr(s.pos()), s)); return Err((MadeProgress, EExpr::DefMissingFinalExpr(s.pos()), s));
} }
@ -798,7 +796,6 @@ fn parse_defs_end<'a>(
match space0_after_e( match space0_after_e(
crate::pattern::loc_pattern_help(min_indent), crate::pattern::loc_pattern_help(min_indent),
min_indent, min_indent,
EPattern::Space,
EPattern::IndentEnd, EPattern::IndentEnd,
) )
.parse(arena, state.clone()) .parse(arena, state.clone())
@ -815,7 +812,6 @@ fn parse_defs_end<'a>(
let parse_def_expr = space0_before_e( let parse_def_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s), move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
); );
@ -842,7 +838,6 @@ fn parse_defs_end<'a>(
let parse_def_expr = space0_before_e( let parse_def_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s), move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
); );
@ -864,7 +859,6 @@ fn parse_defs_end<'a>(
space0_before_e( space0_before_e(
type_annotation::located_help(min_indent + 1, false), type_annotation::located_help(min_indent + 1, false),
min_indent + 1, min_indent + 1,
EType::TSpace,
EType::TIndentStart, EType::TIndentStart,
), ),
) )
@ -902,7 +896,6 @@ fn parse_defs_expr<'a>(
let parse_final_expr = space0_before_e( let parse_final_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s), move |a, s| parse_loc_expr(min_indent, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
); );
@ -936,7 +929,7 @@ fn parse_expr_operator<'a>(
state: State<'a>, state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let (_, spaces_after_operator, state) = let (_, spaces_after_operator, state) =
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?; space0_e(min_indent, EExpr::IndentEnd).parse(arena, state)?;
// a `-` is unary if it is preceded by a space and not followed by a space // a `-` is unary if it is preceded by a space and not followed by a space
@ -961,11 +954,10 @@ fn parse_expr_operator<'a>(
expr_state.initial = state.clone(); expr_state.initial = state.clone();
let (spaces, state) = let (spaces, state) = match space0_e(min_indent, EExpr::IndentEnd).parse(arena, state) {
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) { Err((_, _, state)) => (&[] as &[_], state),
Err((_, _, state)) => (&[] as &[_], state), Ok((_, spaces, state)) => (spaces, state),
Ok((_, spaces, state)) => (spaces, state), };
};
expr_state.arguments.push(arena.alloc(arg)); expr_state.arguments.push(arena.alloc(arg));
expr_state.spaces_after = spaces; expr_state.spaces_after = spaces;
@ -1054,7 +1046,6 @@ fn parse_expr_operator<'a>(
let parse_cont = space0_before_e( let parse_cont = space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s), move |a, s| parse_loc_expr(min_indent, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
); );
@ -1094,7 +1085,6 @@ fn parse_expr_operator<'a>(
space0_before_e( space0_before_e(
type_annotation::located_help(indented_more, true), type_annotation::located_help(indented_more, true),
min_indent, min_indent,
EType::TSpace,
EType::TIndentStart, EType::TIndentStart,
), ),
) )
@ -1123,7 +1113,6 @@ fn parse_expr_operator<'a>(
space0_before_e( space0_before_e(
type_annotation::located_help(indented_more, false), type_annotation::located_help(indented_more, false),
min_indent, min_indent,
EType::TSpace,
EType::TIndentStart, EType::TIndentStart,
), ),
); );
@ -1180,7 +1169,7 @@ fn parse_expr_operator<'a>(
.with_spaces_before(spaces_after_operator, new_expr.region); .with_spaces_before(spaces_after_operator, new_expr.region);
} }
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) { match space0_e(min_indent, EExpr::IndentEnd).parse(arena, state) {
Err((_, _, state)) => { Err((_, _, state)) => {
let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena));
@ -1243,7 +1232,7 @@ fn parse_expr_end<'a>(
} }
expr_state.initial = state.clone(); expr_state.initial = state.clone();
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) { match space0_e(min_indent, EExpr::IndentEnd).parse(arena, state) {
Err((_, _, state)) => { Err((_, _, state)) => {
expr_state.arguments.push(arena.alloc(arg)); expr_state.arguments.push(arena.alloc(arg));
expr_state.end = new_end; expr_state.end = new_end;
@ -1290,7 +1279,6 @@ fn parse_expr_end<'a>(
space0_around_ee( space0_around_ee(
crate::pattern::loc_pattern_help(min_indent), crate::pattern::loc_pattern_help(min_indent),
min_indent, min_indent,
EPattern::Space,
EPattern::Start, EPattern::Start,
EPattern::IndentEnd, EPattern::IndentEnd,
), ),
@ -1316,7 +1304,6 @@ fn parse_expr_end<'a>(
let parse_body = space0_before_e( let parse_body = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s), move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
); );
@ -1325,7 +1312,6 @@ fn parse_expr_end<'a>(
let parse_cont = space0_before_e( let parse_cont = space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s), move |a, s| parse_loc_expr(min_indent, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
); );
@ -1538,7 +1524,7 @@ pub fn defs<'a>(min_indent: u32) -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, EExpr
}; };
let (_, initial_space, state) = let (_, initial_space, state) =
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?; space0_e(min_indent, EExpr::IndentEnd).parse(arena, state)?;
let start_column = state.column(); let start_column = state.column();
@ -1550,7 +1536,7 @@ pub fn defs<'a>(min_indent: u32) -> impl Parser<'a, Vec<'a, Loc<Def<'a>>>, EExpr
let (_, def_state, state) = parse_defs_end(options, start_column, def_state, arena, state)?; let (_, def_state, state) = parse_defs_end(options, start_column, def_state, arena, state)?;
let (_, final_space, state) = let (_, final_space, state) =
space0_e(start_column, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?; space0_e(start_column, EExpr::IndentEnd).parse(arena, state)?;
let mut output = Vec::with_capacity_in(def_state.defs.len(), arena); let mut output = Vec::with_capacity_in(def_state.defs.len(), arena);
@ -1601,7 +1587,6 @@ fn closure_help<'a>(
space0_around_ee( space0_around_ee(
specialize(ELambda::Pattern, loc_closure_param(min_indent)), specialize(ELambda::Pattern, loc_closure_param(min_indent)),
min_indent, min_indent,
ELambda::Space,
ELambda::IndentArg, ELambda::IndentArg,
ELambda::IndentArrow ELambda::IndentArrow
), ),
@ -1616,7 +1601,6 @@ fn closure_help<'a>(
parse_loc_expr_with_options(min_indent, options, arena, state) parse_loc_expr_with_options(min_indent, options, arena, state)
}), }),
min_indent, min_indent,
ELambda::Space,
ELambda::IndentBody ELambda::IndentBody
) )
) )
@ -1649,7 +1633,6 @@ mod when {
parse_loc_expr_with_options(min_indent, options, arena, state) parse_loc_expr_with_options(min_indent, options, arena, state)
}), }),
min_indent, min_indent,
EWhen::Space,
EWhen::IndentCondition, EWhen::IndentCondition,
EWhen::IndentIs, EWhen::IndentIs,
), ),
@ -1797,7 +1780,6 @@ mod when {
parse_loc_expr_with_options(min_indent + 1, options, arena, state) parse_loc_expr_with_options(min_indent + 1, options, arena, state)
}), }),
min_indent, min_indent,
EWhen::Space,
EWhen::IndentIfGuard, EWhen::IndentIfGuard,
EWhen::IndentArrow, EWhen::IndentArrow,
) )
@ -1814,13 +1796,11 @@ mod when {
) -> impl Parser<'a, Loc<Pattern<'a>>, EWhen<'a>> { ) -> impl Parser<'a, Loc<Pattern<'a>>, EWhen<'a>> {
move |arena, state| { move |arena, state| {
let (_, spaces, state) = let (_, spaces, state) =
backtrackable(space0_e(min_indent, EWhen::Space, EWhen::IndentPattern)) backtrackable(space0_e(min_indent, EWhen::IndentPattern)).parse(arena, state)?;
.parse(arena, state)?;
let (_, loc_pattern, state) = space0_after_e( let (_, loc_pattern, state) = space0_after_e(
specialize(EWhen::Pattern, crate::pattern::loc_pattern_help(min_indent)), specialize(EWhen::Pattern, crate::pattern::loc_pattern_help(min_indent)),
min_indent, min_indent,
EWhen::Space,
EWhen::IndentPattern, EWhen::IndentPattern,
) )
.parse(arena, state)?; .parse(arena, state)?;
@ -1847,7 +1827,7 @@ mod when {
let initial = state.clone(); let initial = state.clone();
// put no restrictions on the indent after the spaces; we'll check it manually // put no restrictions on the indent after the spaces; we'll check it manually
match space0_e(0, EWhen::Space, EWhen::IndentPattern).parse(arena, state) { match space0_e(0, EWhen::IndentPattern).parse(arena, state) {
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)), Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)), Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
Ok((_progress, spaces, state)) => { Ok((_progress, spaces, state)) => {
@ -1914,7 +1894,6 @@ mod when {
indent, arena, state indent, arena, state
)), )),
indent, indent,
EWhen::Space,
EWhen::IndentBranch, EWhen::IndentBranch,
) )
) )
@ -1929,7 +1908,6 @@ fn if_branch<'a>(min_indent: u32) -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a
parse_loc_expr(min_indent, arena, state) parse_loc_expr(min_indent, arena, state)
}), }),
min_indent, min_indent,
EIf::Space,
EIf::IndentCondition, EIf::IndentCondition,
EIf::IndentThenToken, EIf::IndentThenToken,
) )
@ -1945,7 +1923,6 @@ fn if_branch<'a>(min_indent: u32) -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a
parse_loc_expr(min_indent, arena, state) parse_loc_expr(min_indent, arena, state)
}), }),
min_indent, min_indent,
EIf::Space,
EIf::IndentThenBranch, EIf::IndentThenBranch,
EIf::IndentElseToken, EIf::IndentElseToken,
) )
@ -1975,7 +1952,6 @@ fn expect_help<'a>(
parse_loc_expr_with_options(start_column + 1, options, arena, state) parse_loc_expr_with_options(start_column + 1, options, arena, state)
}), }),
start_column + 1, start_column + 1,
EExpect::Space,
EExpect::IndentCondition, EExpect::IndentCondition,
) )
.parse(arena, state) .parse(arena, state)
@ -1986,7 +1962,6 @@ fn expect_help<'a>(
space0_before_e( space0_before_e(
move |a, s| parse_loc_expr(min_indent, a, s), move |a, s| parse_loc_expr(min_indent, a, s),
min_indent, min_indent,
EExpr::Space,
EExpr::IndentEnd, EExpr::IndentEnd,
), ),
); );
@ -2018,7 +1993,7 @@ fn if_expr_help<'a>(
// try to parse another `if` // try to parse another `if`
// NOTE this drops spaces between the `else` and the `if` // NOTE this drops spaces between the `else` and the `if`
let optional_if = and!( let optional_if = and!(
backtrackable(space0_e(min_indent, EIf::Space, EIf::IndentIf)), backtrackable(space0_e(min_indent, EIf::IndentIf)),
parser::keyword_e(keyword::IF, EIf::If) parser::keyword_e(keyword::IF, EIf::If)
); );
@ -2036,7 +2011,6 @@ fn if_expr_help<'a>(
parse_loc_expr_with_options(min_indent, options, arena, state) parse_loc_expr_with_options(min_indent, options, arena, state)
}), }),
min_indent, min_indent,
EIf::Space,
EIf::IndentElseBranch, EIf::IndentElseBranch,
) )
.parse(arena, state_final_else) .parse(arena, state_final_else)
@ -2133,7 +2107,6 @@ fn list_literal_help<'a>(min_indent: u32) -> impl Parser<'a, Expr<'a>, EList<'a>
word1(b']', EList::End), word1(b']', EList::End),
min_indent, min_indent,
EList::Open, EList::Open,
EList::Space,
EList::IndentEnd, EList::IndentEnd,
Expr::SpaceBefore Expr::SpaceBefore
) )
@ -2158,8 +2131,7 @@ fn record_field_help<'a>(
.parse(arena, state)?; .parse(arena, state)?;
debug_assert_eq!(progress, MadeProgress); debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) = let (_, spaces, state) = space0_e(min_indent, ERecord::IndentColon).parse(arena, state)?;
space0_e(min_indent, ERecord::Space, ERecord::IndentColon).parse(arena, state)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work. // Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.) // (This is true in both literals and types.)
@ -2173,7 +2145,6 @@ fn record_field_help<'a>(
parse_loc_expr_no_multi_backpassing(min_indent, a, s) parse_loc_expr_no_multi_backpassing(min_indent, a, s)
}), }),
min_indent, min_indent,
ERecord::Space,
ERecord::IndentEnd, ERecord::IndentEnd,
) )
)) ))
@ -2236,7 +2207,6 @@ fn record_help<'a>(
// (and not e.g. an `Expr::Access`) and extract its string. // (and not e.g. an `Expr::Access`) and extract its string.
loc!(record_updateable_identifier()), loc!(record_updateable_identifier()),
min_indent, min_indent,
ERecord::Space,
ERecord::IndentEnd, ERecord::IndentEnd,
ERecord::IndentAmpersand, ERecord::IndentAmpersand,
), ),
@ -2254,12 +2224,11 @@ fn record_help<'a>(
space0_around_ee( space0_around_ee(
loc!(record_field_help(min_indent)), loc!(record_field_help(min_indent)),
min_indent, min_indent,
ERecord::Space,
ERecord::IndentEnd, ERecord::IndentEnd,
ERecord::IndentEnd ERecord::IndentEnd
), ),
), ),
space0_e(min_indent, ERecord::Space, ERecord::IndentEnd) space0_e(min_indent, ERecord::IndentEnd)
), ),
word1(b'}', ERecord::End) word1(b'}', ERecord::End)
) )

View file

@ -255,11 +255,7 @@ pub fn package_entry<'a>() -> impl Parser<'a, Spaced<'a, PackageEntry<'a>>, EPac
specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()), specialize(|_, pos| EPackageEntry::Shorthand(pos), lowercase_ident()),
word1(b':', EPackageEntry::Colon) word1(b':', EPackageEntry::Colon)
), ),
space0_e( space0_e(min_indent, EPackageEntry::IndentPackage)
min_indent,
EPackageEntry::Space,
EPackageEntry::IndentPackage
)
)) ))
.parse(arena, state)?; .parse(arena, state)?;

View file

@ -9,7 +9,7 @@ use crate::parser::Progress::{self, *};
use crate::parser::{ use crate::parser::{
backtrackable, optional, specialize, specialize_region, word1, EExposes, EGenerates, backtrackable, optional, specialize, specialize_region, word1, EExposes, EGenerates,
EGeneratesWith, EHeader, EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser, EGeneratesWith, EHeader, EImports, EPackages, EProvides, ERequires, ETypedIdent, Parser,
SourceError, SyntaxError, SourceError, SpaceProblem, SyntaxError,
}; };
use crate::state::State; use crate::state::State;
use crate::string_literal; use crate::string_literal;
@ -57,7 +57,7 @@ fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
map!( map!(
and!( and!(
space0_e(0, EHeader::Space, EHeader::IndentStart), space0_e(0, EHeader::IndentStart),
one_of![ one_of![
map!( map!(
skip_first!(keyword_e("interface", EHeader::Start), interface_header()), skip_first!(keyword_e("interface", EHeader::Start), interface_header()),
@ -107,7 +107,7 @@ fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'a>> {
let min_indent = 1; let min_indent = 1;
let (_, after_interface_keyword, state) = let (_, after_interface_keyword, state) =
space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?; let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?;
let (_, ((before_exposes, after_exposes), exposes), state) = let (_, ((before_exposes, after_exposes), exposes), state) =
@ -137,7 +137,7 @@ fn hosted_header<'a>() -> impl Parser<'a, HostedHeader<'a>, EHeader<'a>> {
let min_indent = 1; let min_indent = 1;
let (_, after_hosted_keyword, state) = let (_, after_hosted_keyword, state) =
space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?; let (_, name, state) = loc!(module_name_help(EHeader::ModuleName)).parse(arena, state)?;
let (_, ((before_exposes, after_exposes), exposes), state) = let (_, ((before_exposes, after_exposes), exposes), state) =
@ -236,7 +236,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
let min_indent = 1; let min_indent = 1;
let (_, after_app_keyword, state) = let (_, after_app_keyword, state) =
space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = loc!(crate::parser::specialize( let (_, name, state) = loc!(crate::parser::specialize(
EHeader::AppName, EHeader::AppName,
string_literal::parse() string_literal::parse()
@ -303,7 +303,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, EHeader<'a>> {
let min_indent = 1; let min_indent = 1;
let (_, after_platform_keyword, state) = let (_, after_platform_keyword, state) =
space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?; space0_e(min_indent, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = let (_, name, state) =
loc!(specialize(EHeader::PlatformName, package_name())).parse(arena, state)?; loc!(specialize(EHeader::PlatformName, package_name())).parse(arena, state)?;
@ -380,7 +380,6 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides<'a>> {
min_indent, min_indent,
"to", "to",
EProvides::To, EProvides::To,
EProvides::Space,
EProvides::IndentTo, EProvides::IndentTo,
EProvides::IndentListStart EProvides::IndentListStart
), ),
@ -422,7 +421,6 @@ fn provides_without_to<'a>() -> impl Parser<
min_indent, min_indent,
"provides", "provides",
EProvides::Provides, EProvides::Provides,
EProvides::Space,
EProvides::IndentProvides, EProvides::IndentProvides,
EProvides::IndentListStart EProvides::IndentListStart
), ),
@ -434,7 +432,6 @@ fn provides_without_to<'a>() -> impl Parser<
word1(b']', EProvides::ListEnd), word1(b']', EProvides::ListEnd),
min_indent, min_indent,
EProvides::Open, EProvides::Open,
EProvides::Space,
EProvides::IndentListEnd, EProvides::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
), ),
@ -468,7 +465,6 @@ fn provides_types<'a>(
word1(b'}', EProvides::ListEnd), word1(b'}', EProvides::ListEnd),
min_indent, min_indent,
EProvides::Open, EProvides::Open,
EProvides::Space,
EProvides::IndentListEnd, EProvides::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -518,7 +514,6 @@ fn requires<'a>() -> impl Parser<
min_indent, min_indent,
"requires", "requires",
ERequires::Requires, ERequires::Requires,
ERequires::Space,
ERequires::IndentRequires, ERequires::IndentRequires,
ERequires::IndentListStart ERequires::IndentListStart
), ),
@ -530,10 +525,7 @@ fn requires<'a>() -> impl Parser<
fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> { fn platform_requires<'a>() -> impl Parser<'a, PlatformRequires<'a>, ERequires<'a>> {
map!( map!(
and!( and!(
skip_second!( skip_second!(requires_rigids(0), space0_e(0, ERequires::ListStart)),
requires_rigids(0),
space0_e(0, ERequires::Space, ERequires::ListStart)
),
requires_typed_ident() requires_typed_ident()
), ),
|(rigids, signature)| { PlatformRequires { rigids, signature } } |(rigids, signature)| { PlatformRequires { rigids, signature } }
@ -554,7 +546,6 @@ fn requires_rigids<'a>(
word1(b'}', ERequires::ListEnd), word1(b'}', ERequires::ListEnd),
min_indent, min_indent,
ERequires::Open, ERequires::Open,
ERequires::Space,
ERequires::IndentListEnd, ERequires::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -568,7 +559,6 @@ fn requires_typed_ident<'a>() -> impl Parser<'a, Loc<Spaced<'a, TypedIdent<'a>>>
space0_around_ee( space0_around_ee(
specialize(ERequires::TypedIdent, loc!(typed_ident()),), specialize(ERequires::TypedIdent, loc!(typed_ident()),),
0, 0,
ERequires::Space,
ERequires::ListStart, ERequires::ListStart,
ERequires::ListEnd ERequires::ListEnd
), ),
@ -593,7 +583,6 @@ fn exposes_values<'a>() -> impl Parser<
min_indent, min_indent,
"exposes", "exposes",
EExposes::Exposes, EExposes::Exposes,
EExposes::Space,
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
@ -604,7 +593,6 @@ fn exposes_values<'a>() -> impl Parser<
word1(b']', EExposes::ListEnd), word1(b']', EExposes::ListEnd),
min_indent, min_indent,
EExposes::Open, EExposes::Open,
EExposes::Space,
EExposes::IndentListEnd, EExposes::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -615,19 +603,18 @@ fn spaces_around_keyword<'a, E>(
min_indent: u32, min_indent: u32,
keyword: &'static str, keyword: &'static str,
expectation: fn(Position) -> E, expectation: fn(Position) -> E,
space_problem: fn(crate::parser::BadInputError, Position) -> E,
indent_problem1: fn(Position) -> E, indent_problem1: fn(Position) -> E,
indent_problem2: fn(Position) -> E, indent_problem2: fn(Position) -> E,
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), E> ) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]), E>
where where
E: 'a, E: 'a + SpaceProblem,
{ {
and!( and!(
skip_second!( skip_second!(
backtrackable(space0_e(min_indent, space_problem, indent_problem1)), backtrackable(space0_e(min_indent, indent_problem1)),
crate::parser::keyword_e(keyword, expectation) crate::parser::keyword_e(keyword, expectation)
), ),
space0_e(min_indent, space_problem, indent_problem2) space0_e(min_indent, indent_problem2)
) )
} }
@ -647,7 +634,6 @@ fn exposes_modules<'a>() -> impl Parser<
min_indent, min_indent,
"exposes", "exposes",
EExposes::Exposes, EExposes::Exposes,
EExposes::Space,
EExposes::IndentExposes, EExposes::IndentExposes,
EExposes::IndentListStart EExposes::IndentListStart
), ),
@ -658,7 +644,6 @@ fn exposes_modules<'a>() -> impl Parser<
word1(b']', EExposes::ListEnd), word1(b']', EExposes::ListEnd),
min_indent, min_indent,
EExposes::Open, EExposes::Open,
EExposes::Space,
EExposes::IndentListEnd, EExposes::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -696,7 +681,6 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
min_indent, min_indent,
"packages", "packages",
EPackages::Packages, EPackages::Packages,
EPackages::Space,
EPackages::IndentPackages, EPackages::IndentPackages,
EPackages::IndentListStart EPackages::IndentListStart
), ),
@ -707,7 +691,6 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, EPackages<'a>> {
word1(b'}', EPackages::ListEnd), word1(b'}', EPackages::ListEnd),
min_indent, min_indent,
EPackages::Open, EPackages::Open,
EPackages::Space,
EPackages::IndentListEnd, EPackages::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -741,7 +724,6 @@ fn generates<'a>() -> impl Parser<
min_indent, min_indent,
"generates", "generates",
EGenerates::Generates, EGenerates::Generates,
EGenerates::Space,
EGenerates::IndentGenerates, EGenerates::IndentGenerates,
EGenerates::IndentTypeStart EGenerates::IndentTypeStart
), ),
@ -765,7 +747,6 @@ fn generates_with<'a>() -> impl Parser<
min_indent, min_indent,
"with", "with",
EGeneratesWith::With, EGeneratesWith::With,
EGeneratesWith::Space,
EGeneratesWith::IndentWith, EGeneratesWith::IndentWith,
EGeneratesWith::IndentListStart EGeneratesWith::IndentListStart
), ),
@ -776,7 +757,6 @@ fn generates_with<'a>() -> impl Parser<
word1(b']', EGeneratesWith::ListEnd), word1(b']', EGeneratesWith::ListEnd),
min_indent, min_indent,
EGeneratesWith::Open, EGeneratesWith::Open,
EGeneratesWith::Space,
EGeneratesWith::IndentListEnd, EGeneratesWith::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -799,7 +779,6 @@ fn imports<'a>() -> impl Parser<
min_indent, min_indent,
"imports", "imports",
EImports::Imports, EImports::Imports,
EImports::Space,
EImports::IndentImports, EImports::IndentImports,
EImports::IndentListStart EImports::IndentListStart
), ),
@ -810,7 +789,6 @@ fn imports<'a>() -> impl Parser<
word1(b']', EImports::ListEnd), word1(b']', EImports::ListEnd),
min_indent, min_indent,
EImports::Open, EImports::Open,
EImports::Space,
EImports::IndentListEnd, EImports::IndentListEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )
@ -831,7 +809,7 @@ fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<
|_, pos| ETypedIdent::Identifier(pos), |_, pos| ETypedIdent::Identifier(pos),
lowercase_ident() lowercase_ident()
)), )),
space0_e(min_indent, ETypedIdent::Space, ETypedIdent::IndentHasType) space0_e(min_indent, ETypedIdent::IndentHasType)
), ),
skip_first!( skip_first!(
word1(b':', ETypedIdent::HasType), word1(b':', ETypedIdent::HasType),
@ -841,7 +819,6 @@ fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<
type_annotation::located_help(min_indent, true) type_annotation::located_help(min_indent, true)
), ),
min_indent, min_indent,
ETypedIdent::Space,
ETypedIdent::IndentType, ETypedIdent::IndentType,
) )
) )
@ -899,7 +876,6 @@ fn imports_entry<'a>() -> impl Parser<'a, Spaced<'a, ImportsEntry<'a>>, EImports
word1(b'}', EImports::SetEnd), word1(b'}', EImports::SetEnd),
min_indent, min_indent,
EImports::Open, EImports::Open,
EImports::Space,
EImports::IndentSetEnd, EImports::IndentSetEnd,
Spaced::SpaceBefore Spaced::SpaceBefore
) )

View file

@ -63,6 +63,50 @@ pub enum SyntaxError<'a> {
Space(BadInputError), Space(BadInputError),
NotEndOfFile(Position), NotEndOfFile(Position),
} }
pub trait SpaceProblem {
fn space_problem(e: BadInputError, pos: Position) -> Self;
}
macro_rules! impl_space_problem {
($($name:ident $(< $lt:tt >)?),*) => {
$(
impl $(< $lt >)? SpaceProblem for $name $(< $lt >)? {
fn space_problem(e: BadInputError, pos: Position) -> Self {
Self::Space(e, pos)
}
}
)*
};
}
impl_space_problem! {
EExpect<'a>,
EExposes,
EExpr<'a>,
EGenerates,
EGeneratesWith,
EHeader<'a>,
EIf<'a>,
EImports,
EInParens<'a>,
ELambda<'a>,
EList<'a>,
EPackageEntry<'a>,
EPackages<'a>,
EPattern<'a>,
EProvides<'a>,
ERecord<'a>,
ERequires<'a>,
EString<'a>,
EType<'a>,
ETypeInParens<'a>,
ETypeRecord<'a>,
ETypeTagUnion<'a>,
ETypedIdent<'a>,
EWhen<'a>,
PInParens<'a>,
PRecord<'a>
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum EHeader<'a> { pub enum EHeader<'a> {
@ -505,6 +549,8 @@ pub enum PInParens<'a> {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum EType<'a> { pub enum EType<'a> {
Space(BadInputError, Position),
TRecord(ETypeRecord<'a>, Position), TRecord(ETypeRecord<'a>, Position),
TTagUnion(ETypeTagUnion<'a>, Position), TTagUnion(ETypeTagUnion<'a>, Position),
TInParens(ETypeInParens<'a>, Position), TInParens(ETypeInParens<'a>, Position),
@ -516,7 +562,6 @@ pub enum EType<'a> {
/// ///
TStart(Position), TStart(Position),
TEnd(Position), TEnd(Position),
TSpace(BadInputError, Position),
TFunctionArgument(Position), TFunctionArgument(Position),
/// ///
TIndentStart(Position), TIndentStart(Position),
@ -1153,11 +1198,11 @@ macro_rules! collection {
#[macro_export] #[macro_export]
macro_rules! collection_trailing_sep_e { macro_rules! collection_trailing_sep_e {
($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $space_problem:expr, $indent_problem:expr, $space_before:expr) => { ($opening_brace:expr, $elem:expr, $delimiter:expr, $closing_brace:expr, $min_indent:expr, $open_problem:expr, $indent_problem:expr, $space_before:expr) => {
skip_first!( skip_first!(
$opening_brace, $opening_brace,
|arena, state| { |arena, state| {
let (_, spaces, state) = space0_e($min_indent, $space_problem, $indent_problem) let (_, spaces, state) = space0_e($min_indent, $indent_problem)
.parse(arena, state)?; .parse(arena, state)?;
let (_, (mut parsed_elems, mut final_comments), state) = let (_, (mut parsed_elems, mut final_comments), state) =
@ -1167,12 +1212,11 @@ macro_rules! collection_trailing_sep_e {
$crate::blankspace::space0_before_optional_after( $crate::blankspace::space0_before_optional_after(
$elem, $elem,
$min_indent, $min_indent,
$space_problem,
$indent_problem, $indent_problem,
$indent_problem $indent_problem
) )
), ),
$crate::blankspace::space0_e($min_indent, $space_problem, $indent_problem) $crate::blankspace::space0_e($min_indent, $indent_problem)
).parse(arena, state)?; ).parse(arena, state)?;
let (_,_, state) = let (_,_, state) =

View file

@ -75,8 +75,7 @@ fn loc_tag_pattern_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>,
// If we encounter one, we're done parsing function args! // If we encounter one, we're done parsing function args!
move |arena, state| { move |arena, state| {
let (_, spaces, state) = let (_, spaces, state) =
backtrackable(space0_e(min_indent, EPattern::Space, EPattern::IndentStart)) backtrackable(space0_e(min_indent, EPattern::IndentStart)).parse(arena, state)?;
.parse(arena, state)?;
let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?; let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?;
@ -123,7 +122,6 @@ fn loc_pattern_in_parens_help<'a>(
move |arena, state| specialize_ref(PInParens::Pattern, loc_pattern_help(min_indent)) move |arena, state| specialize_ref(PInParens::Pattern, loc_pattern_help(min_indent))
.parse(arena, state), .parse(arena, state),
min_indent, min_indent,
PInParens::Space,
PInParens::IndentOpen, PInParens::IndentOpen,
PInParens::IndentEnd, PInParens::IndentEnd,
), ),
@ -321,7 +319,6 @@ fn record_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Pattern<'a>, PRec
word1(b'}', PRecord::End), word1(b'}', PRecord::End),
min_indent, min_indent,
PRecord::Open, PRecord::Open,
PRecord::Space,
PRecord::IndentEnd, PRecord::IndentEnd,
Pattern::SpaceBefore Pattern::SpaceBefore
) )
@ -347,8 +344,7 @@ fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
.parse(arena, state)?; .parse(arena, state)?;
debug_assert_eq!(progress, MadeProgress); debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) = let (_, spaces, state) = space0_e(min_indent, PRecord::IndentEnd).parse(arena, state)?;
space0_e(min_indent, PRecord::Space, PRecord::IndentEnd).parse(arena, state)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work. // Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.) // (This is true in both literals and types.)
@ -362,7 +358,7 @@ fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
Some(First(_)) => { Some(First(_)) => {
let val_parser = specialize_ref(PRecord::Pattern, loc_pattern_help(min_indent)); let val_parser = specialize_ref(PRecord::Pattern, loc_pattern_help(min_indent));
let (_, loc_val, state) = let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon) space0_before_e(val_parser, min_indent, PRecord::IndentColon)
.parse(arena, state)?; .parse(arena, state)?;
let Loc { let Loc {
@ -392,7 +388,7 @@ fn record_pattern_field<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
}); });
let (_, loc_val, state) = let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon) space0_before_e(val_parser, min_indent, PRecord::IndentColon)
.parse(arena, state)?; .parse(arena, state)?;
let Loc { let Loc {

View file

@ -28,7 +28,6 @@ fn tag_union_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ET
word1(b']', ETypeTagUnion::End), word1(b']', ETypeTagUnion::End),
min_indent, min_indent,
ETypeTagUnion::Open, ETypeTagUnion::Open,
ETypeTagUnion::Space,
ETypeTagUnion::IndentEnd, ETypeTagUnion::IndentEnd,
Tag::SpaceBefore Tag::SpaceBefore
) )
@ -87,16 +86,11 @@ fn check_type_alias(
fn parse_type_alias_after_as<'a>(min_indent: u32) -> impl Parser<'a, AliasHeader<'a>, EType<'a>> { fn parse_type_alias_after_as<'a>(min_indent: u32) -> impl Parser<'a, AliasHeader<'a>, EType<'a>> {
move |arena, state| { move |arena, state| {
space0_before_e( space0_before_e(term(min_indent), min_indent, EType::TAsIndentStart)
term(min_indent), .parse(arena, state)
min_indent, .and_then(|(p, annot, state)| {
EType::TSpace, specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state)
EType::TAsIndentStart, })
)
.parse(arena, state)
.and_then(|(p, annot, state)| {
specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state)
})
} }
} }
@ -122,7 +116,7 @@ fn term<'a>(min_indent: u32) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'
map!( map!(
and!( and!(
skip_second!( skip_second!(
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentEnd)), backtrackable(space0_e(min_indent, EType::TIndentEnd)),
crate::parser::keyword_e(keyword::AS, EType::TEnd) crate::parser::keyword_e(keyword::AS, EType::TEnd)
), ),
parse_type_alias_after_as(min_indent) parse_type_alias_after_as(min_indent)
@ -170,7 +164,7 @@ fn loc_applied_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<TypeAnnotation<'a
map_with_arena!( map_with_arena!(
and!( and!(
backtrackable(space0_e(min_indent, EType::TSpace, EType::TIndentStart)), backtrackable(space0_e(min_indent, EType::TIndentStart)),
one_of!( one_of!(
loc_wildcard(), loc_wildcard(),
loc_inferred(), loc_inferred(),
@ -201,7 +195,6 @@ fn loc_type_in_parens<'a>(
move |arena, state| specialize_ref(ETypeInParens::Type, expression(min_indent, true)) move |arena, state| specialize_ref(ETypeInParens::Type, expression(min_indent, true))
.parse(arena, state), .parse(arena, state),
min_indent, min_indent,
ETypeInParens::Space,
ETypeInParens::IndentOpen, ETypeInParens::IndentOpen,
ETypeInParens::IndentEnd, ETypeInParens::IndentEnd,
), ),
@ -263,7 +256,7 @@ fn record_type_field<'a>(
debug_assert_eq!(progress, MadeProgress); debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) = let (_, spaces, state) =
space0_e(min_indent, ETypeRecord::Space, ETypeRecord::IndentEnd).parse(arena, state)?; space0_e(min_indent, ETypeRecord::IndentEnd).parse(arena, state)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work. // Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.) // (This is true in both literals and types.)
@ -277,13 +270,9 @@ fn record_type_field<'a>(
match opt_loc_val { match opt_loc_val {
Some(First(_)) => { Some(First(_)) => {
let (_, loc_val, state) = space0_before_e( let (_, loc_val, state) =
val_parser, space0_before_e(val_parser, min_indent, ETypeRecord::IndentColon)
min_indent, .parse(arena, state)?;
ETypeRecord::Space,
ETypeRecord::IndentColon,
)
.parse(arena, state)?;
Ok(( Ok((
MadeProgress, MadeProgress,
@ -292,13 +281,9 @@ fn record_type_field<'a>(
)) ))
} }
Some(Second(_)) => { Some(Second(_)) => {
let (_, loc_val, state) = space0_before_e( let (_, loc_val, state) =
val_parser, space0_before_e(val_parser, min_indent, ETypeRecord::IndentOptional)
min_indent, .parse(arena, state)?;
ETypeRecord::Space,
ETypeRecord::IndentOptional,
)
.parse(arena, state)?;
Ok(( Ok((
MadeProgress, MadeProgress,
@ -336,7 +321,6 @@ fn record_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, EType
word1(b'}', ETypeRecord::End), word1(b'}', ETypeRecord::End),
min_indent, min_indent,
ETypeRecord::Open, ETypeRecord::Open,
ETypeRecord::Space,
ETypeRecord::IndentEnd, ETypeRecord::IndentEnd,
AssignedField::SpaceBefore AssignedField::SpaceBefore
) )
@ -389,13 +373,8 @@ fn expression<'a>(
is_trailing_comma_valid: bool, is_trailing_comma_valid: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> { ) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
(move |arena, state: State<'a>| { (move |arena, state: State<'a>| {
let (p1, first, state) = space0_before_e( let (p1, first, state) = space0_before_e(term(min_indent), min_indent, EType::TIndentStart)
term(min_indent), .parse(arena, state)?;
min_indent,
EType::TSpace,
EType::TIndentStart,
)
.parse(arena, state)?;
let result = and![ let result = and![
zero_or_more!(skip_first!( zero_or_more!(skip_first!(
@ -404,7 +383,6 @@ fn expression<'a>(
space0_around_ee( space0_around_ee(
term(min_indent), term(min_indent),
min_indent, min_indent,
EType::TSpace,
EType::TIndentStart, EType::TIndentStart,
EType::TIndentEnd EType::TIndentEnd
), ),
@ -419,7 +397,7 @@ fn expression<'a>(
// TODO this space0 is dropped, so newlines just before the function arrow when there // TODO this space0 is dropped, so newlines just before the function arrow when there
// is only one argument are not seen by the formatter. Can we do better? // is only one argument are not seen by the formatter. Can we do better?
skip_second!( skip_second!(
space0_e(min_indent, EType::TSpace, EType::TIndentStart), space0_e(min_indent, EType::TIndentStart),
word2(b'-', b'>', EType::TStart) word2(b'-', b'>', EType::TStart)
) )
.trace("type_annotation:expression:arrow") .trace("type_annotation:expression:arrow")
@ -428,13 +406,9 @@ fn expression<'a>(
match result { match result {
Ok((p2, (rest, _dropped_spaces), state)) => { Ok((p2, (rest, _dropped_spaces), state)) => {
let (p3, return_type, state) = space0_before_e( let (p3, return_type, state) =
term(min_indent), space0_before_e(term(min_indent), min_indent, EType::TIndentStart)
min_indent, .parse(arena, state)?;
EType::TSpace,
EType::TIndentStart,
)
.parse(arena, state)?;
// prepare arguments // prepare arguments
let mut arguments = Vec::with_capacity_in(rest.len() + 1, arena); let mut arguments = Vec::with_capacity_in(rest.len() + 1, arena);
@ -452,7 +426,7 @@ fn expression<'a>(
Err(err) => { Err(err) => {
if !is_trailing_comma_valid { if !is_trailing_comma_valid {
let (_, comma, _) = optional(skip_first!( let (_, comma, _) = optional(skip_first!(
space0_e(min_indent, EType::TSpace, EType::TIndentStart), space0_e(min_indent, EType::TIndentStart),
word1(b',', EType::TStart) word1(b',', EType::TStart)
)) ))
.trace("check trailing comma") .trace("check trailing comma")

View file

@ -1,2 +1 @@
interface T exposes [] imports [] interface T exposes [] imports []

View file

@ -895,8 +895,11 @@ fn type_to_variable<'a>(
let tag_union_var = register(subs, rank, pools, content); let tag_union_var = register(subs, rank, pools, content);
subs.set_content( register_with_known_var(
subs,
*rec_var, *rec_var,
rank,
pools,
Content::RecursionVar { Content::RecursionVar {
opt_name: None, opt_name: None,
structure: tag_union_var, structure: tag_union_var,
@ -2030,3 +2033,22 @@ fn register(subs: &mut Subs, rank: Rank, pools: &mut Pools, content: Content) ->
var var
} }
fn register_with_known_var(
subs: &mut Subs,
var: Variable,
rank: Rank,
pools: &mut Pools,
content: Content,
) {
let descriptor = Descriptor {
content,
rank,
mark: Mark::NONE,
copy: OptVariable::NONE,
};
subs.set(var, descriptor);
pools.get_mut(rank).push(var);
}

View file

@ -10,7 +10,6 @@ mod helpers;
#[cfg(test)] #[cfg(test)]
mod solve_expr { mod solve_expr {
use crate::helpers::with_larger_debug_stack; use crate::helpers::with_larger_debug_stack;
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_types::pretty_print::{content_to_string, name_all_type_vars}; use roc_types::pretty_print::{content_to_string, name_all_type_vars};
@ -64,7 +63,6 @@ mod solve_expr {
dir.path(), dir.path(),
exposed_types, exposed_types,
roc_target::TargetInfo::default_x86_64(), roc_target::TargetInfo::default_x86_64(),
builtin_defs_map,
); );
dir.close()?; dir.close()?;
@ -3036,7 +3034,6 @@ mod solve_expr {
} }
#[test] #[test]
#[ignore]
fn typecheck_mutually_recursive_tag_union_2() { fn typecheck_mutually_recursive_tag_union_2() {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
@ -3064,7 +3061,6 @@ mod solve_expr {
} }
#[test] #[test]
#[ignore]
fn typecheck_mutually_recursive_tag_union_listabc() { fn typecheck_mutually_recursive_tag_union_listabc() {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
@ -5196,4 +5192,63 @@ mod solve_expr {
r#"{ bi128 : I128 -> I128, bi16 : I16 -> I16, bi32 : I32 -> I32, bi64 : I64 -> I64, bi8 : I8 -> I8, bnat : Nat -> Nat, bu128 : U128 -> U128, bu16 : U16 -> U16, bu32 : U32 -> U32, bu64 : U64 -> U64, bu8 : U8 -> U8, dec : Dec -> Dec, f32 : F32 -> F32, f64 : F64 -> F64, fdec : Dec -> Dec, ff32 : F32 -> F32, ff64 : F64 -> F64, i128 : I128 -> I128, i16 : I16 -> I16, i32 : I32 -> I32, i64 : I64 -> I64, i8 : I8 -> I8, nat : Nat -> Nat, u128 : U128 -> U128, u16 : U16 -> U16, u32 : U32 -> U32, u64 : U64 -> U64, u8 : U8 -> U8 }"#, r#"{ bi128 : I128 -> I128, bi16 : I16 -> I16, bi32 : I32 -> I32, bi64 : I64 -> I64, bi8 : I8 -> I8, bnat : Nat -> Nat, bu128 : U128 -> U128, bu16 : U16 -> U16, bu32 : U32 -> U32, bu64 : U64 -> U64, bu8 : U8 -> U8, dec : Dec -> Dec, f32 : F32 -> F32, f64 : F64 -> F64, fdec : Dec -> Dec, ff32 : F32 -> F32, ff64 : F64 -> F64, i128 : I128 -> I128, i16 : I16 -> I16, i32 : I32 -> I32, i64 : I64 -> I64, i8 : I8 -> I8, nat : Nat -> Nat, u128 : U128 -> U128, u16 : U16 -> U16, u32 : U32 -> U32, u64 : U64 -> U64, u8 : U8 -> U8 }"#,
) )
} }
#[test]
fn issue_2458() {
infer_eq_without_problem(
indoc!(
r#"
Foo a : [ Blah (Result (Bar a) { val: a }) ]
Bar a : Foo a
v : Bar U8
v = Blah (Ok (Blah (Err { val: 1 })))
v
"#
),
"Bar U8",
)
}
// https://github.com/rtfeldman/roc/issues/2379
#[test]
fn copy_vars_referencing_copied_vars() {
infer_eq_without_problem(
indoc!(
r#"
Job : [ Job [ Command ] (List Job) ]
job : Job
job
"#
),
"Job",
)
}
#[test]
fn copy_vars_referencing_copied_vars_specialized() {
infer_eq_without_problem(
indoc!(
r#"
Job a : [ Job [ Command ] (Job a) (List (Job a)) a ]
job : Job Str
when job is
Job _ j lst _ ->
when j is
Job _ _ _ s ->
{ j, lst, s }
"#
),
// TODO: this means that we're doing our job correctly, as now both `Job a`s have been
// specialized to the same type, and the second destructuring proves the reified type
// is `Job Str`. But we should just print the structure of the recursive type directly.
// See https://github.com/rtfeldman/roc/issues/2513
"{ j : a, lst : List a, s : Str }",
)
}
} }

View file

@ -99,7 +99,7 @@ fn bool_list_literal() {
true : Bool true : Bool
true = True true = True
List.repeat 23 true List.repeat true 23
"# "#
), ),
RocList::from_slice(&[true; 23]), RocList::from_slice(&[true; 23]),
@ -112,7 +112,7 @@ fn bool_list_literal() {
true : Bool true : Bool
true = True true = True
List.repeat 23 { x: true, y: true } List.repeat { x: true, y: true } 23
"# "#
), ),
RocList::from_slice(&[[true, true]; 23]), RocList::from_slice(&[[true, true]; 23]),
@ -125,7 +125,7 @@ fn bool_list_literal() {
true : Bool true : Bool
true = True true = True
List.repeat 23 { x: true, y: true, a: true, b: true, c: true, d : true, e: true, f: true } List.repeat { x: true, y: true, a: true, b: true, c: true, d : true, e: true, f: true } 23
"# "#
), ),
RocList::from_slice(&[[true, true, true, true, true, true, true, true]; 23]), RocList::from_slice(&[[true, true, true, true, true, true, true, true]; 23]),
@ -1240,18 +1240,18 @@ fn list_single() {
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn list_repeat() { fn list_repeat() {
assert_evals_to!( assert_evals_to!(
"List.repeat 5 1", "List.repeat 1 5",
RocList::from_slice(&[1, 1, 1, 1, 1]), RocList::from_slice(&[1, 1, 1, 1, 1]),
RocList<i64> RocList<i64>
); );
assert_evals_to!( assert_evals_to!(
"List.repeat 4 2", "List.repeat 2 4",
RocList::from_slice(&[2, 2, 2, 2]), RocList::from_slice(&[2, 2, 2, 2]),
RocList<i64> RocList<i64>
); );
assert_evals_to!( assert_evals_to!(
"List.repeat 2 []", "List.repeat [] 2",
RocList::from_slice(&[RocList::default(), RocList::default()]), RocList::from_slice(&[RocList::default(), RocList::default()]),
RocList<RocList<i64>> RocList<RocList<i64>>
); );
@ -1263,7 +1263,7 @@ fn list_repeat() {
noStrs = noStrs =
[] []
List.repeat 2 noStrs List.repeat noStrs 2
"# "#
), ),
RocList::from_slice(&[RocList::default(), RocList::default()]), RocList::from_slice(&[RocList::default(), RocList::default()]),
@ -1271,7 +1271,7 @@ fn list_repeat() {
); );
assert_evals_to!( assert_evals_to!(
"List.repeat 15 4", "List.repeat 4 15",
RocList::from_slice(&[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]), RocList::from_slice(&[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]),
RocList<i64> RocList<i64>
); );
@ -2363,7 +2363,7 @@ fn list_keep_errs() {
#[cfg(any(feature = "gen-llvm"))] #[cfg(any(feature = "gen-llvm"))]
fn list_map_with_index() { fn list_map_with_index() {
assert_evals_to!( assert_evals_to!(
"List.mapWithIndex [0,0,0] (\\index, x -> Num.intCast index + x)", "List.mapWithIndex [0,0,0] (\\x, index -> Num.intCast index + x)",
RocList::from_slice(&[0, 1, 2]), RocList::from_slice(&[0, 1, 2]),
RocList<i64> RocList<i64>
); );

View file

@ -2490,3 +2490,41 @@ fn monomorphized_ints_aliased() {
u8 u8
) )
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn to_float_f32() {
assert_evals_to!(
indoc!(
r#"
n : U8
n = 100
f : F32
f = Num.toFloat n
f
"#
),
100.,
f32
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn to_float_f64() {
assert_evals_to!(
indoc!(
r#"
n : U8
n = 100
f : F64
f = Num.toFloat n
f
"#
),
100.,
f64
)
}

View file

@ -1474,3 +1474,83 @@ fn issue_2445() {
i64 i64
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn issue_2458() {
assert_evals_to!(
indoc!(
r#"
Foo a : [ Blah (Bar a), Nothing {} ]
Bar a : Foo a
v : Bar {}
v = Blah (Blah (Nothing {}))
when v is
Blah (Blah (Nothing {})) -> 15
_ -> 25
"#
),
15,
u8
)
}
#[test]
#[ignore = "See https://github.com/rtfeldman/roc/issues/2466"]
#[cfg(any(feature = "gen-llvm"))]
fn issue_2458_deep_recursion_var() {
assert_evals_to!(
indoc!(
r#"
Foo a : [ Blah (Result (Bar a) {}) ]
Bar a : Foo a
v : Bar {}
when v is
Blah (Ok (Blah (Err {}))) -> "1"
_ -> "2"
"#
),
15,
u8
)
}
#[test]
#[cfg(any(feature = "gen-llvm"))]
fn issue_1162() {
assert_evals_to!(
indoc!(
r#"
app "test" provides [ main ] to "./platform"
RBTree k : [ Node k (RBTree k) (RBTree k), Empty ]
balance : a, RBTree a -> RBTree a
balance = \key, left ->
when left is
Node _ _ lRight ->
Node key lRight Empty
_ ->
Empty
tree : RBTree {}
tree =
balance {} Empty
main : U8
main =
when tree is
Empty -> 15
_ -> 25
"#
),
15,
u8
)
}

View file

@ -1,7 +1,6 @@
use libloading::Library; use libloading::Library;
use roc_build::link::{link, LinkType}; use roc_build::link::{link, LinkType};
use roc_builtins::bitcode; use roc_builtins::bitcode;
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_region::all::LineInfo; use roc_region::all::LineInfo;
use tempfile::tempdir; use tempfile::tempdir;
@ -58,7 +57,6 @@ pub fn helper(
src_dir, src_dir,
exposed_types, exposed_types,
roc_target::TargetInfo::default_x86_64(), roc_target::TargetInfo::default_x86_64(),
builtin_defs_map,
); );
let mut loaded = loaded.expect("failed to load module"); let mut loaded = loaded.expect("failed to load module");

View file

@ -3,7 +3,6 @@ use inkwell::module::Module;
use libloading::Library; use libloading::Library;
use roc_build::link::module_to_dylib; use roc_build::link::module_to_dylib;
use roc_build::program::FunctionIterator; use roc_build::program::FunctionIterator;
use roc_can::builtins::builtin_defs_map;
use roc_can::def::Def; use roc_can::def::Def;
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_gen_llvm::llvm::externs::add_default_roc_externs; use roc_gen_llvm::llvm::externs::add_default_roc_externs;
@ -25,9 +24,6 @@ fn promote_expr_to_module(src: &str) -> String {
buffer buffer
} }
pub fn test_builtin_defs(symbol: Symbol, var_store: &mut VarStore) -> Option<Def> {
builtin_defs_map(symbol, var_store)
}
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn create_llvm_module<'a>( fn create_llvm_module<'a>(
@ -67,7 +63,6 @@ fn create_llvm_module<'a>(
src_dir, src_dir,
exposed_types, exposed_types,
target_info, target_info,
test_builtin_defs,
); );
let mut loaded = match loaded { let mut loaded = match loaded {

View file

@ -7,7 +7,6 @@ use std::path::{Path, PathBuf};
use wasmer::{Memory, WasmPtr}; use wasmer::{Memory, WasmPtr};
use crate::helpers::from_wasmer_memory::FromWasmerMemory; use crate::helpers::from_wasmer_memory::FromWasmerMemory;
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_gen_wasm::wasm32_result::Wasm32Result; use roc_gen_wasm::wasm32_result::Wasm32Result;
use roc_gen_wasm::{DEBUG_LOG_SETTINGS, MEMORY_NAME}; use roc_gen_wasm::{DEBUG_LOG_SETTINGS, MEMORY_NAME};
@ -94,7 +93,6 @@ fn compile_roc_to_wasm_bytes<'a, T: Wasm32Result>(
src_dir, src_dir,
exposed_types, exposed_types,
roc_target::TargetInfo::default_wasm32(), roc_target::TargetInfo::default_wasm32(),
builtin_defs_map,
); );
let loaded = loaded.expect("failed to load module"); let loaded = loaded.expect("failed to load module");

View file

@ -18,7 +18,6 @@ const EXPANDED_STACK_SIZE: usize = 8 * 1024 * 1024;
use test_mono_macros::*; use test_mono_macros::*;
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_mono::ir::Proc; use roc_mono::ir::Proc;
@ -107,7 +106,6 @@ fn compiles_to_ir(test_name: &str, src: &str) {
src_dir, src_dir,
exposed_types, exposed_types,
TARGET_INFO, TARGET_INFO,
builtin_defs_map,
); );
let mut loaded = match loaded { let mut loaded = match loaded {

View file

@ -1633,7 +1633,7 @@ roc_error_macros::assert_sizeof_all!(FlatType, 3 * 8);
roc_error_macros::assert_sizeof_aarch64!((Variable, Option<Lowercase>), 4 * 8); roc_error_macros::assert_sizeof_aarch64!((Variable, Option<Lowercase>), 4 * 8);
roc_error_macros::assert_sizeof_wasm!((Variable, Option<Lowercase>), 4 * 4); roc_error_macros::assert_sizeof_wasm!((Variable, Option<Lowercase>), 4 * 4);
roc_error_macros::assert_sizeof_all!((Variable, Option<Lowercase>), 4 * 8); roc_error_macros::assert_sizeof_default!((Variable, Option<Lowercase>), 4 * 8);
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Content { pub enum Content {

View file

@ -3,6 +3,7 @@ use crate::subs::{
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice, GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
}; };
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap}; use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
use roc_error_macros::internal_error;
use roc_module::called_via::CalledVia; use roc_module::called_via::CalledVia;
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
@ -595,9 +596,15 @@ impl Type {
ext.substitute_alias(rep_symbol, rep_args, actual) ext.substitute_alias(rep_symbol, rep_args, actual)
} }
Alias { Alias {
type_arguments,
actual: alias_actual, actual: alias_actual,
.. ..
} => alias_actual.substitute_alias(rep_symbol, rep_args, actual), } => {
for (_, ta) in type_arguments {
ta.substitute_alias(rep_symbol, rep_args, actual)?;
}
alias_actual.substitute_alias(rep_symbol, rep_args, actual)
}
HostExposedAlias { HostExposedAlias {
actual: actual_type, actual: actual_type,
.. ..
@ -809,22 +816,24 @@ impl Type {
substitution.insert(*placeholder, filler); substitution.insert(*placeholder, filler);
} }
// make sure hidden variables are freshly instantiated
let mut lambda_set_variables = let mut lambda_set_variables =
Vec::with_capacity(alias.lambda_set_variables.len()); Vec::with_capacity(alias.lambda_set_variables.len());
for lambda_set in alias.lambda_set_variables.iter() { for typ in alias.lambda_set_variables.iter() {
let fresh = var_store.fresh(); if let Type::Variable(var) = typ.0 {
introduced.insert(fresh); let fresh = var_store.fresh();
introduced.insert(fresh);
lambda_set_variables.push(LambdaSet(Type::Variable(fresh))); substitution.insert(var, Type::Variable(fresh));
lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
if let Type::Variable(lambda_set_var) = lambda_set.0 { } else {
substitution.insert(lambda_set_var, Type::Variable(fresh)); unreachable!("at this point there should be only vars in there");
} }
} }
actual.substitute(&substitution);
actual.instantiate_aliases(region, aliases, var_store, introduced); actual.instantiate_aliases(region, aliases, var_store, introduced);
actual.substitute(&substitution);
// instantiate recursion variable! // instantiate recursion variable!
if let Type::RecursiveTagUnion(rec_var, mut tags, mut ext) = actual { if let Type::RecursiveTagUnion(rec_var, mut tags, mut ext) = actual {
let new_rec_var = var_store.fresh(); let new_rec_var = var_store.fresh();
@ -836,20 +845,15 @@ impl Type {
} }
ext.substitute(&substitution); ext.substitute(&substitution);
*self = Type::Alias { actual = Type::RecursiveTagUnion(new_rec_var, tags, ext);
symbol: *symbol,
type_arguments: named_args,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(Type::RecursiveTagUnion(new_rec_var, tags, ext)),
};
} else {
*self = Type::Alias {
symbol: *symbol,
type_arguments: named_args,
lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(actual),
};
} }
*self = Type::Alias {
symbol: *symbol,
type_arguments: named_args,
lambda_set_variables,
actual: Box::new(actual),
};
} else { } else {
// one of the special-cased Apply types. // one of the special-cased Apply types.
for x in args { for x in args {
@ -863,6 +867,61 @@ impl Type {
EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {} EmptyRec | EmptyTagUnion | ClosureTag { .. } | Erroneous(_) | Variable(_) => {}
} }
} }
pub fn is_tag_union_like(&self) -> bool {
matches!(
self,
Type::TagUnion(..)
| Type::RecursiveTagUnion(..)
| Type::FunctionOrTagUnion(..)
| Type::EmptyTagUnion
)
}
/// We say a type is "narrow" if no type composing it is a proper sum; that is, no type
/// composing it is a tag union with more than one variant.
///
/// The types checked here must have all of their non-builtin `Apply`s instantiated, as a
/// non-instantiated `Apply` would be ambiguous.
///
/// The following are narrow:
///
/// ```roc
/// U8
/// [ A I8 ]
/// [ A [ B [ C U8 ] ] ]
/// [ A (R a) ] as R a
/// ```
///
/// The following are not:
///
/// ```roc
/// [ A I8, B U8 ]
/// [ A [ B [ Result U8 {} ] ] ] (Result U8 {} is actually [ Ok U8, Err {} ])
/// [ A { lst: List (R a) } ] as R a (List a is morally [ Cons (List a), Nil ] as List a)
/// ```
pub fn is_narrow(&self) -> bool {
match self.shallow_dealias() {
Type::TagUnion(tags, ext) | Type::RecursiveTagUnion(_, tags, ext) => {
ext.is_empty_tag_union()
&& tags.len() == 1
&& tags[0].1.len() == 1
&& tags[0].1[0].is_narrow()
}
Type::Record(fields, ext) => {
fields.values().all(|field| field.as_inner().is_narrow()) && ext.is_narrow()
}
Type::Function(args, clos, ret) => {
args.iter().all(|a| a.is_narrow()) && clos.is_narrow() && ret.is_narrow()
}
// Lists and sets are morally two-tagged unions, as they can be empty
Type::Apply(Symbol::LIST_LIST | Symbol::SET_SET, _, _) => false,
Type::Apply(..) => internal_error!("cannot chase an Apply!"),
Type::Alias { .. } => internal_error!("should be dealiased"),
// Non-composite types are trivially narrow
_ => true,
}
}
} }
fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) { fn symbols_help(tipe: &Type, accum: &mut ImSet<Symbol>) {

View file

@ -5,7 +5,6 @@ use def::defs_to_html;
use docs_error::DocsResult; use docs_error::DocsResult;
use expr::expr_to_html; use expr::expr_to_html;
use roc_builtins::std::StdLib; use roc_builtins::std::StdLib;
use roc_can::builtins::builtin_defs_map;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_load::docs::DocEntry::DocDef; use roc_load::docs::DocEntry::DocDef;
@ -429,7 +428,6 @@ pub fn load_modules_for_files(filenames: Vec<PathBuf>, std_lib: StdLib) -> Vec<L
src_dir.as_path(), src_dir.as_path(),
MutMap::default(), MutMap::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
builtin_defs_map,
) { ) {
Ok(loaded) => modules.push(loaded), Ok(loaded) => modules.push(loaded),
Err(LoadingProblem::FormattedReport(report)) => { Err(LoadingProblem::FormattedReport(report)) => {

1
editor/.gitignore vendored
View file

@ -1 +0,0 @@

View file

@ -1,4 +1,3 @@
struct VertexOutput { struct VertexOutput {
[[location(0)]] color: vec4<f32>; [[location(0)]] color: vec4<f32>;
[[builtin(position)]] position: vec4<f32>; [[builtin(position)]] position: vec4<f32>;

View file

@ -57,7 +57,7 @@ getWidths = \encoders, initial ->
encode : Encoder -> List U8 encode : Encoder -> List U8
encode = \encoder -> encode = \encoder ->
output = List.repeat (getWidth encoder) 0 output = List.repeat 0 (getWidth encoder)
encodeHelp encoder 0 output encodeHelp encoder 0 output
|> .output |> .output

View file

@ -18,12 +18,12 @@ echo = \shout ->
silence = \length -> silence = \length ->
spaceInUtf8 = 32 spaceInUtf8 = 32
List.repeat length spaceInUtf8 List.repeat spaceInUtf8 length
shout shout
|> Str.toUtf8 |> Str.toUtf8
|> List.mapWithIndex |> List.mapWithIndex
(\i, _ -> (\_, i ->
length = (List.len (Str.toUtf8 shout) - i) length = (List.len (Str.toUtf8 shout) - i)
phrase = (List.split (Str.toUtf8 shout) length).before phrase = (List.split (Str.toUtf8 shout) length).before

View file

@ -81,7 +81,7 @@ with = \path, callback ->
# I cant define scope here and put it in the list in callback. It breaks alias anaysis. # I cant define scope here and put it in the list in callback. It breaks alias anaysis.
# Instead I have to inline this. # Instead I have to inline this.
# root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None } # root_scope = { data: Some handle, index: 0, buf: [], whileInfo: None }
callback { scopes: [ { data: Some handle, index: 0, buf: [], whileInfo: None } ], state: Executing, stack: [], vars: List.repeat Variable.totalCount (Number 0) } callback { scopes: [ { data: Some handle, index: 0, buf: [], whileInfo: None } ], state: Executing, stack: [], vars: List.repeat (Number 0) Variable.totalCount }
# I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is. # I am pretty sure there is a syntax to destructure and keep a reference to the whole, but Im not sure what it is.
getChar : Context -> Task [ T U8 Context ] [ EndOfData, NoScope ]* getChar : Context -> Task [ T U8 Context ] [ EndOfData, NoScope ]*

View file

@ -1,7 +1,6 @@
use bumpalo::Bump; use bumpalo::Bump;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use roc_can::builtins::builtin_defs_map;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_fmt::annotation::Formattable; use roc_fmt::annotation::Formattable;
use roc_fmt::annotation::{Newlines, Parens}; use roc_fmt::annotation::{Newlines, Parens};
@ -65,7 +64,6 @@ pub fn compile_to_mono<'a>(
src_dir, src_dir,
exposed_types, exposed_types,
target_info, target_info,
builtin_defs_map,
); );
let mut loaded = match loaded { let mut loaded = match loaded {

View file

@ -17,8 +17,12 @@ WWW_DIR="repl_www/build"
mkdir -p $WWW_DIR mkdir -p $WWW_DIR
cp repl_www/public/* $WWW_DIR cp repl_www/public/* $WWW_DIR
# Pass all script arguments through to wasm-pack (such as --release) # Pass all script arguments through to wasm-pack
wasm-pack build --target web "$@" repl_wasm # For debugging, pass the --profiling option, which enables optimizations + debug info
# (We need optimizations to get rid of dead code that otherwise causes compile errors!)
cargo build --target wasm32-unknown-unknown -p roc_repl_wasm --release
wasm-bindgen --target web --keep-debug target/wasm32-unknown-unknown/release/roc_repl_wasm.wasm --out-dir repl_wasm/pkg/
# wasm-pack build --target web "$@" repl_wasm
cp repl_wasm/pkg/*.wasm $WWW_DIR cp repl_wasm/pkg/*.wasm $WWW_DIR

View file

@ -148,6 +148,9 @@ pub fn cyclic_alias<'b>(
region: roc_region::all::Region, region: roc_region::all::Region,
others: Vec<Symbol>, others: Vec<Symbol>,
) -> (RocDocBuilder<'b>, String) { ) -> (RocDocBuilder<'b>, String) {
let when_is_recursion_legal =
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive.");
let doc = if others.is_empty() { let doc = if others.is_empty() {
alloc.stack(vec![ alloc.stack(vec![
alloc alloc
@ -155,7 +158,7 @@ pub fn cyclic_alias<'b>(
.append(alloc.symbol_unqualified(symbol)) .append(alloc.symbol_unqualified(symbol))
.append(alloc.reflow(" alias is self-recursive in an invalid way:")), .append(alloc.reflow(" alias is self-recursive in an invalid way:")),
alloc.region(lines.convert_region(region)), alloc.region(lines.convert_region(region)),
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tag."), when_is_recursion_legal,
]) ])
} else { } else {
alloc.stack(vec![ alloc.stack(vec![
@ -179,7 +182,7 @@ pub fn cyclic_alias<'b>(
.map(|other| alloc.symbol_unqualified(other)) .map(|other| alloc.symbol_unqualified(other))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
), ),
alloc.reflow("Recursion in aliases is only allowed if recursion happens behind a tag."), when_is_recursion_legal,
]) ])
}; };

View file

@ -2852,7 +2852,7 @@ mod test_reporting {
^^^ ^^^
Recursion in aliases is only allowed if recursion happens behind a Recursion in aliases is only allowed if recursion happens behind a
tag. tagged union, at least one variant of which is not recursive.
"# "#
), ),
) )
@ -3348,6 +3348,8 @@ mod test_reporting {
{ x, y } { x, y }
"# "#
), ),
// TODO render tag unions across multiple lines
// TODO do not show recursion var if the recursion var does not render on the surface of a type
indoc!( indoc!(
r#" r#"
TYPE MISMATCH TYPE MISMATCH
@ -3360,12 +3362,13 @@ mod test_reporting {
This `ACons` global tag application has the type: This `ACons` global tag application has the type:
[ ACons Num (Integer Signed64) [ BCons (Num a) [ ACons Str [ BNil [ ACons (Num (Integer Signed64)) [
]b ]c ]d, ANil ] BCons (Num (Integer Signed64)) [ ACons Str [ BCons I64 a, BNil ],
ANil ], BNil ], ANil ]
But the type annotation on `x` says it should be: But the type annotation on `x` says it should be:
[ ACons I64 BList I64 I64, ANil ] [ ACons I64 (BList I64 I64), ANil ] as a
"# "#
), ),
) )
@ -7075,7 +7078,7 @@ I need all branches in an `if` to have the same type!
^ ^
Recursion in aliases is only allowed if recursion happens behind a Recursion in aliases is only allowed if recursion happens behind a
tag. tagged union, at least one variant of which is not recursive.
"# "#
), ),
) )
@ -7102,7 +7105,7 @@ I need all branches in an `if` to have the same type!
^ ^
Recursion in aliases is only allowed if recursion happens behind a Recursion in aliases is only allowed if recursion happens behind a
tag. tagged union, at least one variant of which is not recursive.
"# "#
), ),
) )
@ -7128,7 +7131,7 @@ I need all branches in an `if` to have the same type!
^ ^
Recursion in aliases is only allowed if recursion happens behind a Recursion in aliases is only allowed if recursion happens behind a
tag. tagged union, at least one variant of which is not recursive.
"# "#
), ),
) )
@ -7977,4 +7980,111 @@ I need all branches in an `if` to have the same type!
), ),
) )
} }
#[test]
fn recursive_type_alias_is_newtype() {
report_problem_as(
indoc!(
r#"
R a : [ Only (R a) ]
v : R U8
v
"#
),
indoc!(
r#"
CYCLIC ALIAS
The `R` alias is self-recursive in an invalid way:
1 R a : [ Only (R a) ]
^
Recursion in aliases is only allowed if recursion happens behind a
tagged union, at least one variant of which is not recursive.
"#
),
)
}
#[test]
fn recursive_type_alias_is_newtype_deep() {
report_problem_as(
indoc!(
r#"
R a : [ Only { very: [ Deep (R a) ] } ]
v : R U8
v
"#
),
indoc!(
r#"
CYCLIC ALIAS
The `R` alias is self-recursive in an invalid way:
1 R a : [ Only { very: [ Deep (R a) ] } ]
^
Recursion in aliases is only allowed if recursion happens behind a
tagged union, at least one variant of which is not recursive.
"#
),
)
}
#[test]
fn recursive_type_alias_is_newtype_mutual() {
report_problem_as(
indoc!(
r#"
Foo a : [ Thing (Bar a) ]
Bar a : [ Stuff (Foo a) ]
v : Bar U8
v
"#
),
indoc!(
r#"
CYCLIC ALIAS
The `Bar` alias is recursive in an invalid way:
2 Bar a : [ Stuff (Foo a) ]
^^^^^
The `Bar` alias depends on itself through the following chain of
definitions:
Bar
Foo
Recursion in aliases is only allowed if recursion happens behind a
tagged union, at least one variant of which is not recursive.
"#
),
)
}
#[test]
fn issue_2458() {
report_problem_as(
indoc!(
r#"
Foo a : [ Blah (Result (Bar a) []) ]
Bar a : Foo a
v : Bar U8
v
"#
),
"",
)
}
} }

View file

@ -140,7 +140,6 @@ impl<T> RocList<T> {
assert!(capacity > 0); assert!(capacity > 0);
assert!(slice.len() <= capacity); assert!(slice.len() <= capacity);
let ptr = slice.as_ptr();
let element_bytes = capacity * core::mem::size_of::<T>(); let element_bytes = capacity * core::mem::size_of::<T>();
let padding = { let padding = {
@ -165,25 +164,11 @@ impl<T> RocList<T> {
let refcount_ptr = raw_ptr as *mut isize; let refcount_ptr = raw_ptr as *mut isize;
*(refcount_ptr.offset(-1)) = isize::MIN; *(refcount_ptr.offset(-1)) = isize::MIN;
{ // Clone the elements into the new array.
// NOTE: using a memcpy here causes weird issues let target_ptr = raw_ptr;
let target_ptr = raw_ptr as *mut T; for (i, value) in slice.iter().cloned().enumerate() {
let source_ptr = ptr as *const T; let target_ptr = target_ptr.add(i);
for index in 0..slice.len() { target_ptr.write(value);
let source = &*source_ptr.add(index);
let target = &mut *target_ptr.add(index);
// NOTE for a weird reason, it's important that we clone onto the stack
// and explicitly forget the swapped-in value
// cloning directly from source to target causes some garbage memory (cast to a
// RocStr) to end up in the drop implementation of RocStr and cause havoc by
// freeing NULL
let mut temporary = source.clone();
core::mem::swap(target, &mut temporary);
core::mem::forget(temporary);
}
} }
raw_ptr raw_ptr