mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
Merge branch 'main' into i4732
Signed-off-by: Ayaz <20735482+ayazhafiz@users.noreply.github.com>
This commit is contained in:
commit
562a2748cb
746 changed files with 4057 additions and 2826 deletions
|
@ -16,7 +16,7 @@ jobs:
|
||||||
run: curl https://api.github.com/repos/roc-lang/roc/releases > roc_releases.json
|
run: curl https://api.github.com/repos/roc-lang/roc/releases > roc_releases.json
|
||||||
|
|
||||||
- name: get the url of today`s release for macos apple silicon
|
- name: get the url of today`s release for macos apple silicon
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh silicon)" >> $GITHUB_ENV
|
run: echo "RELEASE_URL=$(./ci/get_today_release_url.sh silicon)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Get the archive from the url.
|
- name: Get the archive from the url.
|
||||||
run: curl -OL ${{ env.RELEASE_URL }}
|
run: curl -OL ${{ env.RELEASE_URL }}
|
||||||
|
|
4
.github/workflows/test_nightly_many_os.yml
vendored
4
.github/workflows/test_nightly_many_os.yml
vendored
|
@ -29,11 +29,11 @@ jobs:
|
||||||
|
|
||||||
- name: get the url of today`s release for linux (x86_64)
|
- name: get the url of today`s release for linux (x86_64)
|
||||||
if: startsWith(matrix.os, 'ubuntu')
|
if: startsWith(matrix.os, 'ubuntu')
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh linux_x86_64)" >> $GITHUB_ENV
|
run: echo "RELEASE_URL=$(./ci/get_today_release_url.sh linux_x86_64)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: get the url of today`s release for macos (x86_64)
|
- name: get the url of today`s release for macos (x86_64)
|
||||||
if: startsWith(matrix.os, 'macos')
|
if: startsWith(matrix.os, 'macos')
|
||||||
run: echo "RELEASE_URL=$(./ci/get_latest_release_url.sh macos_x86_64)" >> $GITHUB_ENV
|
run: echo "RELEASE_URL=$(./ci/get_today_release_url.sh macos_x86_64)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: get the archive from the url
|
- name: get the archive from the url
|
||||||
run: curl -OL ${{ env.RELEASE_URL }}
|
run: curl -OL ${{ env.RELEASE_URL }}
|
||||||
|
|
6
.github/workflows/ubuntu_x86_64.yml
vendored
6
.github/workflows/ubuntu_x86_64.yml
vendored
|
@ -36,6 +36,9 @@ jobs:
|
||||||
- name: regular rust tests
|
- name: regular rust tests
|
||||||
run: cargo test --locked --release && sccache --show-stats
|
run: cargo test --locked --release && sccache --show-stats
|
||||||
|
|
||||||
|
- name: test examples/helloWorld.roc separately because it is ignored by default
|
||||||
|
run: cargo test --locked --release -p roc_cli cli_run::hello_world -- --ignored && sccache --show-stats
|
||||||
|
|
||||||
- name: check that the platform`s produced dylib is loadable
|
- name: check that the platform`s produced dylib is loadable
|
||||||
run: cd examples/platform-switching/rust-platform && LD_LIBRARY_PATH=. cargo test --release --locked
|
run: cd examples/platform-switching/rust-platform && LD_LIBRARY_PATH=. cargo test --release --locked
|
||||||
|
|
||||||
|
@ -57,6 +60,9 @@ jobs:
|
||||||
- name: wasm repl test
|
- name: wasm repl test
|
||||||
run: crates/repl_test/test_wasm.sh && sccache --show-stats
|
run: crates/repl_test/test_wasm.sh && sccache --show-stats
|
||||||
|
|
||||||
|
- name: test building wasm repl
|
||||||
|
run: ./ci/www-repl.sh && sccache --show-stats
|
||||||
|
|
||||||
#TODO i386 (32-bit linux) cli tests
|
#TODO i386 (32-bit linux) cli tests
|
||||||
|
|
||||||
#TODO verify-no-git-changes
|
#TODO verify-no-git-changes
|
||||||
|
|
20
Cargo.lock
generated
20
Cargo.lock
generated
|
@ -3602,14 +3602,10 @@ name = "roc_fmt"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc",
|
|
||||||
"pretty_assertions",
|
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_test_utils",
|
|
||||||
"walkdir",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4919,6 +4915,22 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_syntax"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"indoc",
|
||||||
|
"pretty_assertions",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_fmt",
|
||||||
|
"roc_module",
|
||||||
|
"roc_parse",
|
||||||
|
"roc_region",
|
||||||
|
"roc_test_utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
|
|
||||||
LATEST_RELEASE_URL=`cat roc_releases.json | jq --arg arch $1 --arg today $(date +'%Y-%m-%d') '.[0] | .assets | map(.browser_download_url) | map(select(. | contains("\($arch)-\($today)"))) | .[0]'`
|
LATEST_RELEASE_URL=`cat roc_releases.json | jq --arg arch $1 '.[0] | .assets | map(.browser_download_url) | map(select(. | contains("\($arch)-"))) | last'`
|
||||||
|
|
||||||
if [[ "$LATEST_RELEASE_URL" == "null" ]]
|
if [[ "$LATEST_RELEASE_URL" == "null" ]]
|
||||||
then
|
then
|
||||||
echo "I expected to get a url but I got null instead. Today's release may not have been uploaded?"
|
echo "I expected to get a url but I got null instead. The provided argument for this bash script may not have had any matches when used as arch in the jq query above."
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
echo $LATEST_RELEASE_URL | sed s/\"//g
|
echo $LATEST_RELEASE_URL | sed s/\"//g
|
||||||
fi
|
fi
|
||||||
|
|
16
ci/get_today_release_url.sh
Executable file
16
ci/get_today_release_url.sh
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# assumes roc_releases.json is present
|
||||||
|
|
||||||
|
# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/
|
||||||
|
set -euxo pipefail
|
||||||
|
|
||||||
|
TODAY_RELEASE_URL=`cat roc_releases.json | jq --arg arch $1 --arg today $(date +'%Y-%m-%d') '.[0] | .assets | map(.browser_download_url) | map(select(. | contains("\($arch)-\($today)"))) | .[0]'`
|
||||||
|
|
||||||
|
if [[ "$TODAY_RELEASE_URL" == "null" ]]
|
||||||
|
then
|
||||||
|
echo "I expected to get a url but I got null instead. Today's release may not have been uploaded?"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo $TODAY_RELEASE_URL | sed s/\"//g
|
||||||
|
fi
|
|
@ -270,12 +270,13 @@ pub fn build_app<'a>() -> Command<'a> {
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new(CMD_DOCS)
|
Command::new(CMD_DOCS)
|
||||||
.about("Generate documentation for Roc modules (Work In Progress)")
|
.about("Generate documentation for a Roc package")
|
||||||
.arg(Arg::new(DIRECTORY_OR_FILES)
|
.arg(Arg::new(ROC_FILE)
|
||||||
.multiple_values(true)
|
.multiple_values(true)
|
||||||
.required(false)
|
.help("The package's main .roc file")
|
||||||
.help("The directory or files to build documentation for")
|
|
||||||
.allow_invalid_utf8(true)
|
.allow_invalid_utf8(true)
|
||||||
|
.required(false)
|
||||||
|
.default_value(DEFAULT_ROC_FILENAME),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subcommand(Command::new(CMD_GLUE)
|
.subcommand(Command::new(CMD_GLUE)
|
||||||
|
|
|
@ -209,37 +209,9 @@ fn main() -> io::Result<()> {
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
Some((CMD_DOCS, matches)) => {
|
Some((CMD_DOCS, matches)) => {
|
||||||
let maybe_values = matches.values_of_os(DIRECTORY_OR_FILES);
|
let root_filename = matches.value_of_os(ROC_FILE).unwrap();
|
||||||
|
|
||||||
let mut values: Vec<OsString> = Vec::new();
|
generate_docs_html(PathBuf::from(root_filename));
|
||||||
|
|
||||||
match maybe_values {
|
|
||||||
None => {
|
|
||||||
let mut os_string_values: Vec<OsString> = Vec::new();
|
|
||||||
read_all_roc_files(
|
|
||||||
&std::env::current_dir()?.as_os_str().to_os_string(),
|
|
||||||
&mut os_string_values,
|
|
||||||
)?;
|
|
||||||
for os_string in os_string_values {
|
|
||||||
values.push(os_string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(os_values) => {
|
|
||||||
for os_str in os_values {
|
|
||||||
values.push(os_str.to_os_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut roc_files = Vec::new();
|
|
||||||
|
|
||||||
// Populate roc_files
|
|
||||||
for os_str in values {
|
|
||||||
let metadata = fs::metadata(os_str.clone())?;
|
|
||||||
roc_files_recursive(os_str.as_os_str(), metadata.file_type(), &mut roc_files)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_docs_html(roc_files);
|
|
||||||
|
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,7 +473,9 @@ mod cli_run {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial(cli_platform)]
|
#[serial(cli_platform)]
|
||||||
#[cfg_attr(windows, ignore)]
|
#[ignore]
|
||||||
|
// ignored because downloaded prebuilt platforms cause problems with nix and NixOS
|
||||||
|
// this is explicitly tested on CI (.github/workflows/ubuntu_x86_64.yml)
|
||||||
fn hello_world() {
|
fn hello_world() {
|
||||||
test_roc_app_slim(
|
test_roc_app_slim(
|
||||||
"examples",
|
"examples",
|
||||||
|
@ -484,6 +486,19 @@ mod cli_run {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial(cli_platform)]
|
||||||
|
#[cfg_attr(windows, ignore)]
|
||||||
|
fn hello_world_no_url() {
|
||||||
|
test_roc_app_slim(
|
||||||
|
"examples",
|
||||||
|
"helloWorldNoURL.roc",
|
||||||
|
"helloWorld",
|
||||||
|
"Hello, World!\n",
|
||||||
|
UseValgrind::Yes,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const LINE_ENDING: &str = "\r\n";
|
const LINE_ENDING: &str = "\r\n";
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface Bool
|
||||||
## other, and two values `a`, `b` are identical if and only if `isEq a b` is
|
## other, and two values `a`, `b` are identical if and only if `isEq a b` is
|
||||||
## `Bool.true`.
|
## `Bool.true`.
|
||||||
##
|
##
|
||||||
## Not all types support total equality. For example, [F32] and [F64] can
|
## Not all types support total equality. For example, [`F32`](../Num#F32) and [`F64`](../Num#F64) can
|
||||||
## be a `NaN` ([Not a Number](https://en.wikipedia.org/wiki/NaN)), and the
|
## be a `NaN` ([Not a Number](https://en.wikipedia.org/wiki/NaN)), and the
|
||||||
## [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard
|
## [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard
|
||||||
## specifies that two `NaN`s are not equal.
|
## specifies that two `NaN`s are not equal.
|
||||||
|
|
|
@ -150,11 +150,9 @@ interface Num
|
||||||
|
|
||||||
## Represents a number that could be either an [Int] or a [Frac].
|
## Represents a number that could be either an [Int] or a [Frac].
|
||||||
##
|
##
|
||||||
## This is useful for functions that can work on either, for example #Num.add, whose type is:
|
## This is useful for functions that can work on either, for example [Num.add], whose type is:
|
||||||
##
|
##
|
||||||
## ```
|
## add : Num a, Num a -> Num a
|
||||||
## add : Num a, Num a -> Num a
|
|
||||||
## ```
|
|
||||||
##
|
##
|
||||||
## The number 1.5 technically has the type `Num (Fraction *)`, so when you pass
|
## The number 1.5 technically has the type `Num (Fraction *)`, so when you pass
|
||||||
## two of them to [Num.add], the answer you get is `3.0 : Num (Fraction *)`.
|
## two of them to [Num.add], the answer you get is `3.0 : Num (Fraction *)`.
|
||||||
|
|
3
crates/compiler/builtins/roc/main.roc
Normal file
3
crates/compiler/builtins/roc/main.roc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package "builtins"
|
||||||
|
exposes [Str, Num, Bool, Result, List, Dict, Set, Decode, Encode, Hash, Set, Box, Json]
|
||||||
|
packages {}
|
|
@ -149,6 +149,7 @@ pub struct ModuleOutput {
|
||||||
pub rigid_variables: RigidVariables,
|
pub rigid_variables: RigidVariables,
|
||||||
pub declarations: Declarations,
|
pub declarations: Declarations,
|
||||||
pub exposed_imports: MutMap<Symbol, Region>,
|
pub exposed_imports: MutMap<Symbol, Region>,
|
||||||
|
pub exposed_symbols: VecSet<Symbol>,
|
||||||
pub problems: Vec<Problem>,
|
pub problems: Vec<Problem>,
|
||||||
pub referenced_values: VecSet<Symbol>,
|
pub referenced_values: VecSet<Symbol>,
|
||||||
pub referenced_types: VecSet<Symbol>,
|
pub referenced_types: VecSet<Symbol>,
|
||||||
|
@ -280,7 +281,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
aliases: MutMap<Symbol, Alias>,
|
aliases: MutMap<Symbol, Alias>,
|
||||||
imported_abilities_state: PendingAbilitiesStore,
|
imported_abilities_state: PendingAbilitiesStore,
|
||||||
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
exposed_imports: MutMap<Ident, (Symbol, Region)>,
|
||||||
exposed_symbols: &VecSet<Symbol>,
|
exposed_symbols: VecSet<Symbol>,
|
||||||
symbols_from_requires: &[(Loc<Symbol>, Loc<TypeAnnotation<'a>>)],
|
symbols_from_requires: &[(Loc<Symbol>, Loc<TypeAnnotation<'a>>)],
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> ModuleOutput {
|
) -> ModuleOutput {
|
||||||
|
@ -442,7 +443,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
var_store,
|
var_store,
|
||||||
defs,
|
defs,
|
||||||
new_output,
|
new_output,
|
||||||
exposed_symbols,
|
&exposed_symbols,
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
@ -810,6 +811,7 @@ pub fn canonicalize_module_defs<'a>(
|
||||||
pending_derives,
|
pending_derives,
|
||||||
loc_expects: collected.expects,
|
loc_expects: collected.expects,
|
||||||
loc_dbgs: collected.dbgs,
|
loc_dbgs: collected.dbgs,
|
||||||
|
exposed_symbols,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,4 @@ roc_collections = { path = "../collections" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_test_utils = { path = "../../test_utils" }
|
|
||||||
bumpalo.workspace = true
|
bumpalo.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
pretty_assertions.workspace = true
|
|
||||||
indoc.workspace = true
|
|
||||||
walkdir.workspace = true
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "roc_fmt-fuzz"
|
|
||||||
version = "0.0.0"
|
|
||||||
authors = ["Automatically generated"]
|
|
||||||
publish = false
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[package.metadata]
|
|
||||||
cargo-fuzz = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libfuzzer-sys = "0.3"
|
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
|
||||||
roc_parse = { path = "../../parse" }
|
|
||||||
|
|
||||||
[dependencies.roc_fmt]
|
|
||||||
path = ".."
|
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
|
||||||
[workspace]
|
|
||||||
members = ["."]
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "fuzz_expr"
|
|
||||||
path = "fuzz_targets/fuzz_expr.rs"
|
|
||||||
test = false
|
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "fuzz_defs"
|
|
||||||
path = "fuzz_targets/fuzz_defs.rs"
|
|
||||||
test = false
|
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "fuzz_header"
|
|
||||||
path = "fuzz_targets/fuzz_header.rs"
|
|
||||||
test = false
|
|
||||||
doc = false
|
|
|
@ -1,16 +0,0 @@
|
||||||
#![no_main]
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
|
||||||
use roc_parse::test_helpers::parse_expr_with;
|
|
||||||
use roc_fmt::spaces::RemoveSpaces;
|
|
||||||
use roc_fmt::annotation::{Formattable, Parens, Newlines};
|
|
||||||
use roc_fmt::Buf;
|
|
||||||
use roc_fmt::test_helpers::expr_formats;
|
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
|
||||||
if let Ok(input) = std::str::from_utf8(data) {
|
|
||||||
fn expr_formats_to(input: &str, expected: &str) {
|
|
||||||
expr_formats(input, |_| (), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -288,9 +288,18 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
buf.push(')')
|
buf.push(')')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoundVariable(v) => buf.push_str(v),
|
BoundVariable(v) => {
|
||||||
Wildcard => buf.push('*'),
|
buf.indent(indent);
|
||||||
Inferred => buf.push('_'),
|
buf.push_str(v)
|
||||||
|
}
|
||||||
|
Wildcard => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push('*')
|
||||||
|
}
|
||||||
|
Inferred => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push('_')
|
||||||
|
}
|
||||||
|
|
||||||
TagUnion { tags, ext } => {
|
TagUnion { tags, ext } => {
|
||||||
fmt_collection(buf, indent, Braces::Square, *tags, newlines);
|
fmt_collection(buf, indent, Braces::Square, *tags, newlines);
|
||||||
|
@ -355,8 +364,10 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
ann.format_with_options(buf, parens, newlines, indent);
|
ann.format_with_options(buf, parens, newlines, indent);
|
||||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
}
|
}
|
||||||
|
Malformed(raw) => {
|
||||||
Malformed(raw) => buf.push_str(raw),
|
buf.indent(indent);
|
||||||
|
buf.push_str(raw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ pub mod expr;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod pattern;
|
pub mod pattern;
|
||||||
pub mod spaces;
|
pub mod spaces;
|
||||||
pub mod test_helpers;
|
|
||||||
|
|
||||||
use bumpalo::{collections::String, Bump};
|
use bumpalo::{collections::String, Bump};
|
||||||
use roc_parse::ast::Module;
|
use roc_parse::ast::Module;
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
use bumpalo::Bump;
|
|
||||||
use roc_test_utils::assert_multiline_str_eq;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
annotation::{Formattable, Newlines, Parens},
|
|
||||||
Buf,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Parse and re-format the given input, and pass the output to `check_formatting`
|
|
||||||
/// for verification. The expectation is that `check_formatting` assert the result matches
|
|
||||||
/// expectations (or, overwrite the expectation based on a command-line flag)
|
|
||||||
/// Optionally, based on the value of `check_idempotency`, also verify that the formatting
|
|
||||||
/// is idempotent - that if we reformat the output, we get the same result.
|
|
||||||
pub fn expr_formats(input: &str, check_formatting: impl Fn(&str), check_idempotency: bool) {
|
|
||||||
let arena = Bump::new();
|
|
||||||
let input = input.trim();
|
|
||||||
|
|
||||||
match roc_parse::test_helpers::parse_expr_with(&arena, input) {
|
|
||||||
Ok(actual) => {
|
|
||||||
use crate::spaces::RemoveSpaces;
|
|
||||||
|
|
||||||
let mut buf = Buf::new_in(&arena);
|
|
||||||
|
|
||||||
actual.format_with_options(&mut buf, Parens::NotNeeded, Newlines::Yes, 0);
|
|
||||||
|
|
||||||
let output = buf.as_str();
|
|
||||||
|
|
||||||
check_formatting(output);
|
|
||||||
|
|
||||||
let reparsed_ast = roc_parse::test_helpers::parse_expr_with(&arena, output).unwrap_or_else(|err| {
|
|
||||||
panic!(
|
|
||||||
"After formatting, the source code no longer parsed!\n\n\
|
|
||||||
Parse error was: {:?}\n\n\
|
|
||||||
The code that failed to parse:\n\n{}\n\n\
|
|
||||||
The original ast was:\n\n{:#?}\n\n",
|
|
||||||
err, output, actual
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let ast_normalized = actual.remove_spaces(&arena);
|
|
||||||
let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena);
|
|
||||||
|
|
||||||
// HACK!
|
|
||||||
// We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast,
|
|
||||||
// the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same.
|
|
||||||
// I don't have the patience to debug this right now, so let's leave it for another day...
|
|
||||||
// TODO: fix PartialEq impl on ast types
|
|
||||||
if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) {
|
|
||||||
panic!(
|
|
||||||
"Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\
|
|
||||||
* * * Source code before formatting:\n{}\n\n\
|
|
||||||
* * * Source code after formatting:\n{}\n\n\
|
|
||||||
* * * AST before formatting:\n{:#?}\n\n\
|
|
||||||
* * * AST after formatting:\n{:#?}\n\n",
|
|
||||||
input,
|
|
||||||
output,
|
|
||||||
ast_normalized,
|
|
||||||
reparsed_ast_normalized
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now verify that the resultant formatting is _idempotent_ - i.e. that it doesn't change again if re-formatted
|
|
||||||
if check_idempotency {
|
|
||||||
let mut reformatted_buf = Buf::new_in(&arena);
|
|
||||||
reparsed_ast.format_with_options(&mut reformatted_buf, Parens::NotNeeded, Newlines::Yes, 0);
|
|
||||||
|
|
||||||
if output != reformatted_buf.as_str() {
|
|
||||||
eprintln!("Formatting bug; formatting is not stable.\nOriginal code:\n{}\n\nFormatted code:\n{}\n\n", input, output);
|
|
||||||
eprintln!("Reformatting the formatted code changed it again, as follows:\n\n");
|
|
||||||
|
|
||||||
assert_multiline_str_eq!(output, reformatted_buf.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,28 +1,21 @@
|
||||||
use crate::docs::DocEntry::DetachedDoc;
|
use crate::docs::DocEntry::DetachedDoc;
|
||||||
use crate::docs::TypeAnnotation::{Apply, BoundVariable, Function, NoTypeAnn, Record, TagUnion};
|
use crate::docs::TypeAnnotation::{Apply, BoundVariable, Function, NoTypeAnn, Record, TagUnion};
|
||||||
use crate::file::LoadedModule;
|
|
||||||
use roc_can::scope::Scope;
|
use roc_can::scope::Scope;
|
||||||
|
use roc_collections::VecSet;
|
||||||
use roc_module::ident::ModuleName;
|
use roc_module::ident::ModuleName;
|
||||||
use roc_module::symbol::IdentIds;
|
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_parse::ast::AssignedField;
|
use roc_parse::ast::AssignedField;
|
||||||
use roc_parse::ast::{self, ExtractSpaces, TypeHeader};
|
use roc_parse::ast::{self, ExtractSpaces, TypeHeader};
|
||||||
use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef};
|
use roc_parse::ast::{CommentOrNewline, TypeDef, ValueDef};
|
||||||
|
|
||||||
// Documentation generation requirements
|
// Documentation generation requirements
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Documentation {
|
|
||||||
pub name: String,
|
|
||||||
pub version: String,
|
|
||||||
pub docs: String,
|
|
||||||
pub modules: Vec<LoadedModule>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ModuleDocumentation {
|
pub struct ModuleDocumentation {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub entries: Vec<DocEntry>,
|
pub entries: Vec<DocEntry>,
|
||||||
pub scope: Scope,
|
pub scope: Scope,
|
||||||
|
pub exposed_symbols: VecSet<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -34,6 +27,7 @@ pub enum DocEntry {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DocDef {
|
pub struct DocDef {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub symbol: Symbol,
|
||||||
pub type_vars: Vec<String>,
|
pub type_vars: Vec<String>,
|
||||||
pub type_annotation: TypeAnnotation,
|
pub type_annotation: TypeAnnotation,
|
||||||
pub docs: Option<String>,
|
pub docs: Option<String>,
|
||||||
|
@ -98,15 +92,26 @@ pub struct Tag {
|
||||||
|
|
||||||
pub fn generate_module_docs(
|
pub fn generate_module_docs(
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
|
home: ModuleId,
|
||||||
|
module_ids: &ModuleIds,
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
parsed_defs: &roc_parse::ast::Defs,
|
parsed_defs: &roc_parse::ast::Defs,
|
||||||
|
exposed_module_ids: &[ModuleId],
|
||||||
|
exposed_symbols: VecSet<Symbol>,
|
||||||
) -> ModuleDocumentation {
|
) -> ModuleDocumentation {
|
||||||
let entries = generate_entry_docs(&scope.locals.ident_ids, parsed_defs);
|
let entries = generate_entry_docs(
|
||||||
|
home,
|
||||||
|
&scope.locals.ident_ids,
|
||||||
|
module_ids,
|
||||||
|
parsed_defs,
|
||||||
|
exposed_module_ids,
|
||||||
|
);
|
||||||
|
|
||||||
ModuleDocumentation {
|
ModuleDocumentation {
|
||||||
name: module_name.as_str().to_string(),
|
name: module_name.as_str().to_string(),
|
||||||
scope,
|
scope,
|
||||||
entries,
|
entries,
|
||||||
|
exposed_symbols,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,9 +142,12 @@ fn detached_docs_from_comments_and_new_lines<'a>(
|
||||||
detached_docs
|
detached_docs
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_entry_docs<'a>(
|
fn generate_entry_docs(
|
||||||
ident_ids: &'a IdentIds,
|
home: ModuleId,
|
||||||
defs: &roc_parse::ast::Defs<'a>,
|
ident_ids: &IdentIds,
|
||||||
|
module_ids: &ModuleIds,
|
||||||
|
defs: &roc_parse::ast::Defs<'_>,
|
||||||
|
exposed_module_ids: &[ModuleId],
|
||||||
) -> Vec<DocEntry> {
|
) -> Vec<DocEntry> {
|
||||||
use roc_parse::ast::Pattern;
|
use roc_parse::ast::Pattern;
|
||||||
|
|
||||||
|
@ -165,11 +173,12 @@ fn generate_entry_docs<'a>(
|
||||||
Err(value_index) => match &defs.value_defs[value_index.index()] {
|
Err(value_index) => match &defs.value_defs[value_index.index()] {
|
||||||
ValueDef::Annotation(loc_pattern, loc_ann) => {
|
ValueDef::Annotation(loc_pattern, loc_ann) => {
|
||||||
if let Pattern::Identifier(identifier) = loc_pattern.value {
|
if let Pattern::Identifier(identifier) = loc_pattern.value {
|
||||||
// Check if the definition is exposed
|
// Check if this module exposes the def
|
||||||
if ident_ids.get_id(identifier).is_some() {
|
if let Some(ident_id) = ident_ids.get_id(identifier) {
|
||||||
let name = identifier.to_string();
|
let name = identifier.to_string();
|
||||||
let doc_def = DocDef {
|
let doc_def = DocDef {
|
||||||
name,
|
name,
|
||||||
|
symbol: Symbol::new(home, ident_id),
|
||||||
type_annotation: type_to_docs(false, loc_ann.value),
|
type_annotation: type_to_docs(false, loc_ann.value),
|
||||||
type_vars: Vec::new(),
|
type_vars: Vec::new(),
|
||||||
docs,
|
docs,
|
||||||
|
@ -185,12 +194,13 @@ fn generate_entry_docs<'a>(
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if let Pattern::Identifier(identifier) = ann_pattern.value {
|
if let Pattern::Identifier(identifier) = ann_pattern.value {
|
||||||
// Check if the definition is exposed
|
// Check if this module exposes the def
|
||||||
if ident_ids.get_id(identifier).is_some() {
|
if let Some(ident_id) = ident_ids.get_id(identifier) {
|
||||||
let doc_def = DocDef {
|
let doc_def = DocDef {
|
||||||
name: identifier.to_string(),
|
name: identifier.to_string(),
|
||||||
type_annotation: type_to_docs(false, ann_type.value),
|
type_annotation: type_to_docs(false, ann_type.value),
|
||||||
type_vars: Vec::new(),
|
type_vars: Vec::new(),
|
||||||
|
symbol: Symbol::new(home, ident_id),
|
||||||
docs,
|
docs,
|
||||||
};
|
};
|
||||||
acc.push(DocEntry::DocDef(doc_def));
|
acc.push(DocEntry::DocDef(doc_def));
|
||||||
|
@ -226,11 +236,24 @@ fn generate_entry_docs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let type_annotation =
|
||||||
|
// If this alias contains an unexposed type, then don't try to render a
|
||||||
|
// type annotation for it. You're not allowed to see that!
|
||||||
|
// (This comes up when exporting an alias like Task ok err : InnerTask ok err
|
||||||
|
// where Task is exposed but InnerTask isn't.)
|
||||||
|
if contains_unexposed_type(&ann.value, exposed_module_ids, module_ids) {
|
||||||
|
TypeAnnotation::NoTypeAnn
|
||||||
|
} else {
|
||||||
|
type_to_docs(false, ann.value)
|
||||||
|
};
|
||||||
|
|
||||||
|
let ident_id = ident_ids.get_id(name.value).unwrap();
|
||||||
let doc_def = DocDef {
|
let doc_def = DocDef {
|
||||||
name: name.value.to_string(),
|
name: name.value.to_string(),
|
||||||
type_annotation: type_to_docs(false, ann.value),
|
type_annotation,
|
||||||
type_vars,
|
type_vars,
|
||||||
docs,
|
docs,
|
||||||
|
symbol: Symbol::new(home, ident_id),
|
||||||
};
|
};
|
||||||
acc.push(DocEntry::DocDef(doc_def));
|
acc.push(DocEntry::DocDef(doc_def));
|
||||||
}
|
}
|
||||||
|
@ -247,11 +270,13 @@ fn generate_entry_docs<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ident_id = ident_ids.get_id(name.value).unwrap();
|
||||||
let doc_def = DocDef {
|
let doc_def = DocDef {
|
||||||
name: name.value.to_string(),
|
name: name.value.to_string(),
|
||||||
type_annotation: TypeAnnotation::NoTypeAnn,
|
type_annotation: TypeAnnotation::NoTypeAnn,
|
||||||
type_vars,
|
type_vars,
|
||||||
docs,
|
docs,
|
||||||
|
symbol: Symbol::new(home, ident_id),
|
||||||
};
|
};
|
||||||
acc.push(DocEntry::DocDef(doc_def));
|
acc.push(DocEntry::DocDef(doc_def));
|
||||||
}
|
}
|
||||||
|
@ -285,9 +310,11 @@ fn generate_entry_docs<'a>(
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let ident_id = ident_ids.get_id(name.value).unwrap();
|
||||||
let doc_def = DocDef {
|
let doc_def = DocDef {
|
||||||
name: name.value.to_string(),
|
name: name.value.to_string(),
|
||||||
type_annotation: TypeAnnotation::Ability { members },
|
type_annotation: TypeAnnotation::Ability { members },
|
||||||
|
symbol: Symbol::new(home, ident_id),
|
||||||
type_vars,
|
type_vars,
|
||||||
docs,
|
docs,
|
||||||
};
|
};
|
||||||
|
@ -309,6 +336,123 @@ fn generate_entry_docs<'a>(
|
||||||
acc
|
acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Does this type contain any types which are not exposed outside the package?
|
||||||
|
/// (If so, we shouldn't try to render a type annotation for it.)
|
||||||
|
fn contains_unexposed_type(
|
||||||
|
ann: &ast::TypeAnnotation,
|
||||||
|
exposed_module_ids: &[ModuleId],
|
||||||
|
module_ids: &ModuleIds,
|
||||||
|
) -> bool {
|
||||||
|
use ast::TypeAnnotation::*;
|
||||||
|
|
||||||
|
match ann {
|
||||||
|
// Apply is the one case that can directly return true.
|
||||||
|
Apply(module_name, _ident, loc_args) => {
|
||||||
|
// If the *ident* was unexposed, we would have gotten a naming error
|
||||||
|
// during canonicalization, so all we need to check is the module.
|
||||||
|
let module_id = module_ids.get_id(&(*module_name).into()).unwrap();
|
||||||
|
|
||||||
|
!exposed_module_ids.contains(&module_id)
|
||||||
|
|| loc_args.iter().any(|loc_arg| {
|
||||||
|
contains_unexposed_type(&loc_arg.value, exposed_module_ids, module_ids)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Malformed(_) | Inferred | Wildcard | BoundVariable(_) => false,
|
||||||
|
Function(loc_args, loc_ret) => {
|
||||||
|
contains_unexposed_type(&loc_ret.value, exposed_module_ids, module_ids)
|
||||||
|
|| loc_args.iter().any(|loc_arg| {
|
||||||
|
contains_unexposed_type(&loc_arg.value, exposed_module_ids, module_ids)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Record { fields, ext } => {
|
||||||
|
if let Some(loc_ext) = ext {
|
||||||
|
if contains_unexposed_type(&loc_ext.value, exposed_module_ids, module_ids) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fields_to_process =
|
||||||
|
Vec::from_iter(fields.iter().map(|loc_field| loc_field.value));
|
||||||
|
|
||||||
|
while let Some(field) = fields_to_process.pop() {
|
||||||
|
match field {
|
||||||
|
AssignedField::RequiredValue(_field, _spaces, loc_val)
|
||||||
|
| AssignedField::OptionalValue(_field, _spaces, loc_val) => {
|
||||||
|
if contains_unexposed_type(&loc_val.value, exposed_module_ids, module_ids) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AssignedField::Malformed(_) | AssignedField::LabelOnly(_) => {
|
||||||
|
// contains no unexposed types, so continue
|
||||||
|
}
|
||||||
|
AssignedField::SpaceBefore(field, _) | AssignedField::SpaceAfter(field, _) => {
|
||||||
|
fields_to_process.push(*field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Tuple { fields, ext } => {
|
||||||
|
if let Some(loc_ext) = ext {
|
||||||
|
if contains_unexposed_type(&loc_ext.value, exposed_module_ids, module_ids) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.iter().any(|loc_field| {
|
||||||
|
contains_unexposed_type(&loc_field.value, exposed_module_ids, module_ids)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
TagUnion { ext, tags } => {
|
||||||
|
use ast::Tag;
|
||||||
|
|
||||||
|
if let Some(loc_ext) = ext {
|
||||||
|
if contains_unexposed_type(&loc_ext.value, exposed_module_ids, module_ids) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tags_to_process = Vec::from_iter(tags.iter().map(|loc_tag| loc_tag.value));
|
||||||
|
|
||||||
|
while let Some(tag) = tags_to_process.pop() {
|
||||||
|
match tag {
|
||||||
|
Tag::Apply { name: _, args } => {
|
||||||
|
for loc_ann in args.iter() {
|
||||||
|
if contains_unexposed_type(
|
||||||
|
&loc_ann.value,
|
||||||
|
exposed_module_ids,
|
||||||
|
module_ids,
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Tag::Malformed(_) => {
|
||||||
|
// contains no unexposed types, so continue
|
||||||
|
}
|
||||||
|
Tag::SpaceBefore(tag, _) | Tag::SpaceAfter(tag, _) => {
|
||||||
|
tags_to_process.push(*tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Where(loc_ann, _loc_has_clauses) => {
|
||||||
|
// We assume all the abilities in the `has` clause are from exported modules.
|
||||||
|
// TODO don't assume this! Instead, look them up and verify.
|
||||||
|
contains_unexposed_type(&loc_ann.value, exposed_module_ids, module_ids)
|
||||||
|
}
|
||||||
|
As(loc_ann, _spaces, _type_header) => {
|
||||||
|
contains_unexposed_type(&loc_ann.value, exposed_module_ids, module_ids)
|
||||||
|
}
|
||||||
|
SpaceBefore(ann, _) | ast::TypeAnnotation::SpaceAfter(ann, _) => {
|
||||||
|
contains_unexposed_type(ann, exposed_module_ids, module_ids)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> TypeAnnotation {
|
fn type_to_docs(in_func_type_ann: bool, type_annotation: ast::TypeAnnotation) -> TypeAnnotation {
|
||||||
match type_annotation {
|
match type_annotation {
|
||||||
ast::TypeAnnotation::TagUnion { tags, ext } => {
|
ast::TypeAnnotation::TagUnion { tags, ext } => {
|
||||||
|
|
|
@ -144,7 +144,7 @@ struct ModuleCache<'a> {
|
||||||
/// Various information
|
/// Various information
|
||||||
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
||||||
top_level_thunks: MutMap<ModuleId, MutSet<Symbol>>,
|
top_level_thunks: MutMap<ModuleId, MutSet<Symbol>>,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: VecMap<ModuleId, ModuleDocumentation>,
|
||||||
can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
can_problems: MutMap<ModuleId, Vec<roc_problem::can::Problem>>,
|
||||||
type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
type_problems: MutMap<ModuleId, Vec<TypeError>>,
|
||||||
|
|
||||||
|
@ -384,6 +384,7 @@ fn start_phase<'a>(
|
||||||
aliases,
|
aliases,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
skip_constraint_gen,
|
skip_constraint_gen,
|
||||||
|
exposed_module_ids: state.exposed_modules,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -629,7 +630,7 @@ pub struct LoadedModule {
|
||||||
pub resolved_implementations: ResolvedImplementations,
|
pub resolved_implementations: ResolvedImplementations,
|
||||||
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
pub sources: MutMap<ModuleId, (PathBuf, Box<str>)>,
|
||||||
pub timings: MutMap<ModuleId, ModuleTiming>,
|
pub timings: MutMap<ModuleId, ModuleTiming>,
|
||||||
pub documentation: MutMap<ModuleId, ModuleDocumentation>,
|
pub docs_by_module: VecMap<ModuleId, ModuleDocumentation>,
|
||||||
pub abilities_store: AbilitiesStore,
|
pub abilities_store: AbilitiesStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,6 +655,13 @@ impl LoadedModule {
|
||||||
.map(|symbol| symbol.as_str(&self.interns))
|
.map(|symbol| symbol.as_str(&self.interns))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn exposed_aliases_str(&self) -> Vec<&str> {
|
||||||
|
self.exposed_aliases
|
||||||
|
.keys()
|
||||||
|
.map(|symbol| symbol.as_str(&self.interns))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -848,7 +856,7 @@ enum Msg<'a> {
|
||||||
exposed_types_storage: ExposedTypesStorageSubs,
|
exposed_types_storage: ExposedTypesStorageSubs,
|
||||||
resolved_implementations: ResolvedImplementations,
|
resolved_implementations: ResolvedImplementations,
|
||||||
dep_idents: IdentIdsByModule,
|
dep_idents: IdentIdsByModule,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: VecMap<ModuleId, ModuleDocumentation>,
|
||||||
abilities_store: AbilitiesStore,
|
abilities_store: AbilitiesStore,
|
||||||
},
|
},
|
||||||
FoundSpecializations {
|
FoundSpecializations {
|
||||||
|
@ -956,6 +964,10 @@ struct State<'a> {
|
||||||
pub platform_path: PlatformPath<'a>,
|
pub platform_path: PlatformPath<'a>,
|
||||||
pub target_info: TargetInfo,
|
pub target_info: TargetInfo,
|
||||||
|
|
||||||
|
/// Note: only packages and platforms actually expose any modules;
|
||||||
|
/// for all others, this will be empty.
|
||||||
|
pub exposed_modules: &'a [ModuleId],
|
||||||
|
|
||||||
pub module_cache: ModuleCache<'a>,
|
pub module_cache: ModuleCache<'a>,
|
||||||
pub dependencies: Dependencies<'a>,
|
pub dependencies: Dependencies<'a>,
|
||||||
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
pub procedures: MutMap<(Symbol, ProcLayout<'a>), Proc<'a>>,
|
||||||
|
@ -1040,6 +1052,7 @@ impl<'a> State<'a> {
|
||||||
procedures: MutMap::default(),
|
procedures: MutMap::default(),
|
||||||
toplevel_expects: ToplevelExpects::default(),
|
toplevel_expects: ToplevelExpects::default(),
|
||||||
exposed_to_host: ExposedToHost::default(),
|
exposed_to_host: ExposedToHost::default(),
|
||||||
|
exposed_modules: &[],
|
||||||
exposed_types,
|
exposed_types,
|
||||||
arc_modules,
|
arc_modules,
|
||||||
arc_shorthands,
|
arc_shorthands,
|
||||||
|
@ -1151,6 +1164,7 @@ enum BuildTask<'a> {
|
||||||
exposed_symbols: VecSet<Symbol>,
|
exposed_symbols: VecSet<Symbol>,
|
||||||
aliases: MutMap<Symbol, Alias>,
|
aliases: MutMap<Symbol, Alias>,
|
||||||
abilities_store: PendingAbilitiesStore,
|
abilities_store: PendingAbilitiesStore,
|
||||||
|
exposed_module_ids: &'a [ModuleId],
|
||||||
skip_constraint_gen: bool,
|
skip_constraint_gen: bool,
|
||||||
},
|
},
|
||||||
Solve {
|
Solve {
|
||||||
|
@ -2429,13 +2443,20 @@ fn update<'a>(
|
||||||
state.platform_path = PlatformPath::Valid(to_platform);
|
state.platform_path = PlatformPath::Valid(to_platform);
|
||||||
}
|
}
|
||||||
Package {
|
Package {
|
||||||
config_shorthand, ..
|
config_shorthand,
|
||||||
|
exposes_ids,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
|
if header.is_root_module {
|
||||||
|
state.exposed_modules = exposes_ids;
|
||||||
|
}
|
||||||
|
|
||||||
work.extend(state.dependencies.notify_package(config_shorthand));
|
work.extend(state.dependencies.notify_package(config_shorthand));
|
||||||
}
|
}
|
||||||
Platform {
|
Platform {
|
||||||
config_shorthand,
|
config_shorthand,
|
||||||
provides,
|
provides,
|
||||||
|
exposes_ids,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
work.extend(state.dependencies.notify_package(config_shorthand));
|
work.extend(state.dependencies.notify_package(config_shorthand));
|
||||||
|
@ -2469,6 +2490,10 @@ fn update<'a>(
|
||||||
is_prebuilt,
|
is_prebuilt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if header.is_root_module {
|
||||||
|
state.exposed_modules = exposes_ids;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Builtin { .. } | Interface { .. } => {
|
Builtin { .. } | Interface { .. } => {
|
||||||
if header.is_root_module {
|
if header.is_root_module {
|
||||||
|
@ -2748,7 +2773,7 @@ fn update<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let documentation = {
|
let documentation = {
|
||||||
let mut empty = MutMap::default();
|
let mut empty = VecMap::default();
|
||||||
std::mem::swap(&mut empty, &mut state.module_cache.documentation);
|
std::mem::swap(&mut empty, &mut state.module_cache.documentation);
|
||||||
|
|
||||||
empty
|
empty
|
||||||
|
@ -3363,7 +3388,7 @@ fn finish(
|
||||||
exposed_types_storage: ExposedTypesStorageSubs,
|
exposed_types_storage: ExposedTypesStorageSubs,
|
||||||
resolved_implementations: ResolvedImplementations,
|
resolved_implementations: ResolvedImplementations,
|
||||||
dep_idents: IdentIdsByModule,
|
dep_idents: IdentIdsByModule,
|
||||||
documentation: MutMap<ModuleId, ModuleDocumentation>,
|
documentation: VecMap<ModuleId, ModuleDocumentation>,
|
||||||
abilities_store: AbilitiesStore,
|
abilities_store: AbilitiesStore,
|
||||||
) -> LoadedModule {
|
) -> LoadedModule {
|
||||||
let module_ids = Arc::try_unwrap(state.arc_modules)
|
let module_ids = Arc::try_unwrap(state.arc_modules)
|
||||||
|
@ -3411,7 +3436,7 @@ fn finish(
|
||||||
resolved_implementations,
|
resolved_implementations,
|
||||||
sources,
|
sources,
|
||||||
timings: state.timings,
|
timings: state.timings,
|
||||||
documentation,
|
docs_by_module: documentation,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3503,6 +3528,13 @@ fn load_package_from_disk<'a>(
|
||||||
},
|
},
|
||||||
parser_state,
|
parser_state,
|
||||||
)) => {
|
)) => {
|
||||||
|
let exposes_ids = get_exposes_ids(
|
||||||
|
header.exposes.item.items,
|
||||||
|
arena,
|
||||||
|
&module_ids,
|
||||||
|
&ident_ids_by_module,
|
||||||
|
);
|
||||||
|
|
||||||
// make a `platform` module that ultimately exposes `main` to the host
|
// make a `platform` module that ultimately exposes `main` to the host
|
||||||
let (_, _, platform_module_msg) = build_platform_header(
|
let (_, _, platform_module_msg) = build_platform_header(
|
||||||
arena,
|
arena,
|
||||||
|
@ -3511,6 +3543,7 @@ fn load_package_from_disk<'a>(
|
||||||
filename.to_path_buf(),
|
filename.to_path_buf(),
|
||||||
parser_state,
|
parser_state,
|
||||||
module_ids,
|
module_ids,
|
||||||
|
exposes_ids.into_bump_slice(),
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
&header,
|
&header,
|
||||||
pkg_module_timing,
|
pkg_module_timing,
|
||||||
|
@ -3532,6 +3565,32 @@ fn load_package_from_disk<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_exposes_ids<'a>(
|
||||||
|
entries: &'a [Loc<Spaced<'a, roc_parse::header::ModuleName<'a>>>],
|
||||||
|
arena: &'a Bump,
|
||||||
|
module_ids: &Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
|
ident_ids_by_module: &Arc<Mutex<IdentIdsByModule>>,
|
||||||
|
) -> bumpalo::collections::Vec<'a, ModuleId> {
|
||||||
|
let mut exposes_ids = bumpalo::collections::Vec::with_capacity_in(entries.len(), arena);
|
||||||
|
|
||||||
|
// Lock just long enough to perform the minimal operations necessary.
|
||||||
|
let mut module_ids = (**module_ids).lock();
|
||||||
|
let mut ident_ids_by_module = (**ident_ids_by_module).lock();
|
||||||
|
|
||||||
|
// TODO can we "iterate unspaced" instead of calling unspace here?
|
||||||
|
for entry in unspace(arena, entries) {
|
||||||
|
let module_id =
|
||||||
|
module_ids.get_or_insert(&PQModuleName::Unqualified(entry.value.as_str().into()));
|
||||||
|
|
||||||
|
// Ensure this module has an entry in the ident_ids_by_module map.
|
||||||
|
ident_ids_by_module.get_or_insert(module_id);
|
||||||
|
|
||||||
|
exposes_ids.push(module_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
exposes_ids
|
||||||
|
}
|
||||||
|
|
||||||
fn load_builtin_module_help<'a>(
|
fn load_builtin_module_help<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
filename: &str,
|
filename: &str,
|
||||||
|
@ -4061,6 +4120,13 @@ fn parse_header<'a>(
|
||||||
},
|
},
|
||||||
parse_state,
|
parse_state,
|
||||||
)) => {
|
)) => {
|
||||||
|
let exposes_ids = get_exposes_ids(
|
||||||
|
header.exposes.item.items,
|
||||||
|
arena,
|
||||||
|
&module_ids,
|
||||||
|
&ident_ids_by_module,
|
||||||
|
);
|
||||||
|
|
||||||
let (module_id, _, header) = build_platform_header(
|
let (module_id, _, header) = build_platform_header(
|
||||||
arena,
|
arena,
|
||||||
None,
|
None,
|
||||||
|
@ -4068,6 +4134,7 @@ fn parse_header<'a>(
|
||||||
filename,
|
filename,
|
||||||
parse_state,
|
parse_state,
|
||||||
module_ids,
|
module_ids,
|
||||||
|
exposes_ids.into_bump_slice(),
|
||||||
ident_ids_by_module,
|
ident_ids_by_module,
|
||||||
&header,
|
&header,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
@ -4326,7 +4393,7 @@ fn build_header<'a>(
|
||||||
};
|
};
|
||||||
home = module_ids.get_or_insert(&name);
|
home = module_ids.get_or_insert(&name);
|
||||||
|
|
||||||
// Ensure this module has an entry in the exposed_ident_ids map.
|
// Ensure this module has an entry in the ident_ids_by_module map.
|
||||||
ident_ids_by_module.get_or_insert(home);
|
ident_ids_by_module.get_or_insert(home);
|
||||||
|
|
||||||
// For each of our imports, add an entry to deps_by_name
|
// For each of our imports, add an entry to deps_by_name
|
||||||
|
@ -4477,6 +4544,39 @@ fn build_header<'a>(
|
||||||
ident_ids.clone()
|
ident_ids.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This module depends on all the modules it exposes. We need to load those in order
|
||||||
|
// to do things like report errors for them, generate docs for them, etc.
|
||||||
|
if info.is_root_module {
|
||||||
|
if let HeaderType::Platform {
|
||||||
|
exposes,
|
||||||
|
exposes_ids,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| HeaderType::Package {
|
||||||
|
exposes,
|
||||||
|
exposes_ids,
|
||||||
|
..
|
||||||
|
} = header_type
|
||||||
|
{
|
||||||
|
for (loc_module_name, module_id) in exposes.iter().zip(exposes_ids.iter().copied()) {
|
||||||
|
let module_name_str = loc_module_name.value.as_str();
|
||||||
|
let pq_module_name = PackageQualified::Unqualified(module_name_str.into());
|
||||||
|
|
||||||
|
// We should never change an entry here. Either we should have no entry,
|
||||||
|
// or if we do have one, it should be unchanged by this insertion.
|
||||||
|
debug_assert_eq!(
|
||||||
|
&module_id,
|
||||||
|
deps_by_name.get(&pq_module_name).unwrap_or(&module_id),
|
||||||
|
"Already had a deps_by_name entry for {:?}, but it was {:?} rather than the expected {:?}",
|
||||||
|
pq_module_name,
|
||||||
|
deps_by_name.get(&pq_module_name).unwrap(),
|
||||||
|
module_id,
|
||||||
|
);
|
||||||
|
deps_by_name.insert(pq_module_name, module_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let package_entries = packages
|
let package_entries = packages
|
||||||
.iter()
|
.iter()
|
||||||
.map(|Loc { value: pkg, .. }| (pkg.shorthand, pkg.package_name.value))
|
.map(|Loc { value: pkg, .. }| (pkg.shorthand, pkg.package_name.value))
|
||||||
|
@ -5067,10 +5167,17 @@ fn build_package_header<'a>(
|
||||||
arena,
|
arena,
|
||||||
);
|
);
|
||||||
let packages = unspace(arena, header.packages.item.items);
|
let packages = unspace(arena, header.packages.item.items);
|
||||||
|
let exposes_ids = get_exposes_ids(
|
||||||
|
header.exposes.item.items,
|
||||||
|
arena,
|
||||||
|
&module_ids,
|
||||||
|
&ident_ids_by_module,
|
||||||
|
);
|
||||||
let header_type = HeaderType::Package {
|
let header_type = HeaderType::Package {
|
||||||
// A config_shorthand of "" should be fine
|
// A config_shorthand of "" should be fine
|
||||||
config_shorthand: opt_shorthand.unwrap_or_default(),
|
config_shorthand: opt_shorthand.unwrap_or_default(),
|
||||||
exposes: exposes.into_bump_slice(),
|
exposes: exposes.into_bump_slice(),
|
||||||
|
exposes_ids: exposes_ids.into_bump_slice(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let info = HeaderInfo {
|
let info = HeaderInfo {
|
||||||
|
@ -5098,6 +5205,7 @@ fn build_platform_header<'a>(
|
||||||
filename: PathBuf,
|
filename: PathBuf,
|
||||||
parse_state: roc_parse::state::State<'a>,
|
parse_state: roc_parse::state::State<'a>,
|
||||||
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
module_ids: Arc<Mutex<PackageModuleIds<'a>>>,
|
||||||
|
exposes_ids: &'a [ModuleId],
|
||||||
ident_ids_by_module: SharedIdentIdsByModule,
|
ident_ids_by_module: SharedIdentIdsByModule,
|
||||||
header: &PlatformHeader<'a>,
|
header: &PlatformHeader<'a>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
|
@ -5127,6 +5235,7 @@ fn build_platform_header<'a>(
|
||||||
let header_type = HeaderType::Platform {
|
let header_type = HeaderType::Platform {
|
||||||
// A config_shorthand of "" should be fine
|
// A config_shorthand of "" should be fine
|
||||||
config_shorthand: opt_shorthand.unwrap_or_default(),
|
config_shorthand: opt_shorthand.unwrap_or_default(),
|
||||||
|
exposes_ids,
|
||||||
opt_app_module_id,
|
opt_app_module_id,
|
||||||
provides: provides.into_bump_slice(),
|
provides: provides.into_bump_slice(),
|
||||||
exposes: exposes.into_bump_slice(),
|
exposes: exposes.into_bump_slice(),
|
||||||
|
@ -5162,6 +5271,7 @@ fn canonicalize_and_constrain<'a>(
|
||||||
imported_abilities_state: PendingAbilitiesStore,
|
imported_abilities_state: PendingAbilitiesStore,
|
||||||
parsed: ParsedModule<'a>,
|
parsed: ParsedModule<'a>,
|
||||||
skip_constraint_gen: bool,
|
skip_constraint_gen: bool,
|
||||||
|
exposed_module_ids: &[ModuleId],
|
||||||
) -> CanAndCon {
|
) -> CanAndCon {
|
||||||
let canonicalize_start = Instant::now();
|
let canonicalize_start = Instant::now();
|
||||||
|
|
||||||
|
@ -5195,7 +5305,7 @@ fn canonicalize_and_constrain<'a>(
|
||||||
aliases,
|
aliases,
|
||||||
imported_abilities_state,
|
imported_abilities_state,
|
||||||
exposed_imports,
|
exposed_imports,
|
||||||
&exposed_symbols,
|
exposed_symbols,
|
||||||
&symbols_from_requires,
|
&symbols_from_requires,
|
||||||
&mut var_store,
|
&mut var_store,
|
||||||
);
|
);
|
||||||
|
@ -5226,17 +5336,27 @@ fn canonicalize_and_constrain<'a>(
|
||||||
}
|
}
|
||||||
HeaderType::Interface { name, .. }
|
HeaderType::Interface { name, .. }
|
||||||
| HeaderType::Builtin { name, .. }
|
| HeaderType::Builtin { name, .. }
|
||||||
| HeaderType::Hosted { name, .. } => {
|
| HeaderType::Hosted { name, .. }
|
||||||
|
if exposed_module_ids.contains(&parsed.module_id) =>
|
||||||
|
{
|
||||||
let mut scope = module_output.scope.clone();
|
let mut scope = module_output.scope.clone();
|
||||||
scope.add_docs_imports();
|
scope.add_docs_imports();
|
||||||
let docs = crate::docs::generate_module_docs(
|
let docs = crate::docs::generate_module_docs(
|
||||||
scope,
|
scope,
|
||||||
|
module_id,
|
||||||
|
module_ids,
|
||||||
name.as_str().into(),
|
name.as_str().into(),
|
||||||
&parsed_defs_for_docs,
|
&parsed_defs_for_docs,
|
||||||
|
exposed_module_ids,
|
||||||
|
module_output.exposed_symbols.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Some(docs)
|
Some(docs)
|
||||||
}
|
}
|
||||||
|
HeaderType::Interface { .. } | HeaderType::Builtin { .. } | HeaderType::Hosted { .. } => {
|
||||||
|
// This module isn't exposed by the platform, so don't generate docs for it!
|
||||||
|
None
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// _before has an underscore because it's unused in --release builds
|
// _before has an underscore because it's unused in --release builds
|
||||||
|
@ -5301,7 +5421,7 @@ fn canonicalize_and_constrain<'a>(
|
||||||
let module = Module {
|
let module = Module {
|
||||||
module_id,
|
module_id,
|
||||||
exposed_imports: module_output.exposed_imports,
|
exposed_imports: module_output.exposed_imports,
|
||||||
exposed_symbols,
|
exposed_symbols: module_output.exposed_symbols,
|
||||||
referenced_values: module_output.referenced_values,
|
referenced_values: module_output.referenced_values,
|
||||||
referenced_types: module_output.referenced_types,
|
referenced_types: module_output.referenced_types,
|
||||||
aliases,
|
aliases,
|
||||||
|
@ -6079,6 +6199,7 @@ fn run_task<'a>(
|
||||||
aliases,
|
aliases,
|
||||||
abilities_store,
|
abilities_store,
|
||||||
skip_constraint_gen,
|
skip_constraint_gen,
|
||||||
|
exposed_module_ids,
|
||||||
} => {
|
} => {
|
||||||
let can_and_con = canonicalize_and_constrain(
|
let can_and_con = canonicalize_and_constrain(
|
||||||
arena,
|
arena,
|
||||||
|
@ -6089,6 +6210,7 @@ fn run_task<'a>(
|
||||||
abilities_store,
|
abilities_store,
|
||||||
parsed,
|
parsed,
|
||||||
skip_constraint_gen,
|
skip_constraint_gen,
|
||||||
|
exposed_module_ids,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Msg::CanonicalizedAndConstrained(can_and_con))
|
Ok(Msg::CanonicalizedAndConstrained(can_and_con))
|
||||||
|
|
|
@ -281,7 +281,10 @@ impl Interns {
|
||||||
Some(ident_ids) => match ident_ids.get_id(ident.as_str()) {
|
Some(ident_ids) => match ident_ids.get_id(ident.as_str()) {
|
||||||
Some(ident_id) => Symbol::new(module_id, ident_id),
|
Some(ident_id) => Symbol::new(module_id, ident_id),
|
||||||
None => {
|
None => {
|
||||||
panic!("Interns::symbol could not find ident entry for {:?} for module {:?} in Interns {:?}", ident, module_id, self);
|
panic!(
|
||||||
|
"Interns::symbol could not find ident entry for {:?} for module {:?}",
|
||||||
|
ident, module_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
||||||
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
|
Call, CallSpecId, CallType, Expr, HigherOrderLowLevel, JoinPointId, ListLiteralElement,
|
||||||
ModifyRc, Param, Proc, ProcLayout, Stmt,
|
ModifyRc, Param, Proc, ProcLayout, Stmt,
|
||||||
},
|
},
|
||||||
layout::{Builtin, Layout, STLayoutInterner, TagIdIntType, UnionLayout},
|
layout::{Builtin, LambdaSet, Layout, STLayoutInterner, TagIdIntType, UnionLayout},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum UseKind {
|
pub enum UseKind {
|
||||||
|
@ -525,7 +525,11 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||||
});
|
});
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let layout = resolve_recursive_layout(payloads[index as usize], union_layout);
|
let layout = resolve_recursive_layout(
|
||||||
|
self.arena,
|
||||||
|
payloads[index as usize],
|
||||||
|
union_layout,
|
||||||
|
);
|
||||||
Some(layout)
|
Some(layout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -609,7 +613,8 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (arg, wanted_layout) in arguments.iter().zip(payloads.iter()) {
|
for (arg, wanted_layout) in arguments.iter().zip(payloads.iter()) {
|
||||||
let wanted_layout = resolve_recursive_layout(*wanted_layout, union_layout);
|
let wanted_layout =
|
||||||
|
resolve_recursive_layout(self.arena, *wanted_layout, union_layout);
|
||||||
self.check_sym_layout(*arg, wanted_layout, UseKind::TagPayloadArg);
|
self.check_sym_layout(*arg, wanted_layout, UseKind::TagPayloadArg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -626,11 +631,79 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_recursive_layout<'a>(layout: Layout<'a>, when_recursive: UnionLayout<'a>) -> Layout<'a> {
|
fn resolve_recursive_layout<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
when_recursive: UnionLayout<'a>,
|
||||||
|
) -> Layout<'a> {
|
||||||
// TODO check if recursive pointer not in recursive union
|
// TODO check if recursive pointer not in recursive union
|
||||||
match layout {
|
match layout {
|
||||||
Layout::RecursivePointer => Layout::Union(when_recursive),
|
Layout::RecursivePointer => Layout::Union(when_recursive),
|
||||||
other => other,
|
Layout::Union(union_layout) => match union_layout {
|
||||||
|
UnionLayout::NonRecursive(payloads) => {
|
||||||
|
let payloads = payloads.iter().map(|args| {
|
||||||
|
let args = args
|
||||||
|
.iter()
|
||||||
|
.map(|lay| resolve_recursive_layout(arena, *lay, when_recursive));
|
||||||
|
&*arena.alloc_slice_fill_iter(args)
|
||||||
|
});
|
||||||
|
let payloads = arena.alloc_slice_fill_iter(payloads);
|
||||||
|
Layout::Union(UnionLayout::NonRecursive(payloads))
|
||||||
|
}
|
||||||
|
UnionLayout::Recursive(_)
|
||||||
|
| UnionLayout::NonNullableUnwrapped(_)
|
||||||
|
| UnionLayout::NullableWrapped { .. }
|
||||||
|
| UnionLayout::NullableUnwrapped { .. } => {
|
||||||
|
// This is the recursive layout.
|
||||||
|
// TODO will need fixing to be modified once we support multiple
|
||||||
|
// recursive pointers in one structure.
|
||||||
|
layout
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Layout::Boxed(inner) => {
|
||||||
|
Layout::Boxed(arena.alloc(resolve_recursive_layout(arena, *inner, when_recursive)))
|
||||||
|
}
|
||||||
|
Layout::Struct {
|
||||||
|
field_order_hash,
|
||||||
|
field_layouts,
|
||||||
|
} => {
|
||||||
|
let field_layouts = field_layouts
|
||||||
|
.iter()
|
||||||
|
.map(|lay| resolve_recursive_layout(arena, *lay, when_recursive));
|
||||||
|
let field_layouts = arena.alloc_slice_fill_iter(field_layouts);
|
||||||
|
Layout::Struct {
|
||||||
|
field_order_hash,
|
||||||
|
field_layouts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layout::Builtin(builtin) => match builtin {
|
||||||
|
Builtin::List(inner) => {
|
||||||
|
let inner = arena.alloc(resolve_recursive_layout(arena, *inner, when_recursive));
|
||||||
|
Layout::Builtin(Builtin::List(inner))
|
||||||
|
}
|
||||||
|
Builtin::Int(_)
|
||||||
|
| Builtin::Float(_)
|
||||||
|
| Builtin::Bool
|
||||||
|
| Builtin::Decimal
|
||||||
|
| Builtin::Str => layout,
|
||||||
|
},
|
||||||
|
Layout::LambdaSet(LambdaSet {
|
||||||
|
set,
|
||||||
|
representation,
|
||||||
|
}) => {
|
||||||
|
let set = set.iter().map(|(symbol, captures)| {
|
||||||
|
let captures = captures
|
||||||
|
.iter()
|
||||||
|
.map(|lay| resolve_recursive_layout(arena, *lay, when_recursive));
|
||||||
|
let captures = &*arena.alloc_slice_fill_iter(captures);
|
||||||
|
(*symbol, captures)
|
||||||
|
});
|
||||||
|
let set = arena.alloc_slice_fill_iter(set);
|
||||||
|
Layout::LambdaSet(LambdaSet {
|
||||||
|
set,
|
||||||
|
representation,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
use crate::layout::{
|
use crate::layout::{
|
||||||
self, Builtin, CapturesNiche, ClosureCallOptions, ClosureRepresentation, EnumDispatch,
|
self, Builtin, CapturesNiche, ClosureCallOptions, ClosureRepresentation, EnumDispatch,
|
||||||
LambdaName, LambdaSet, Layout, LayoutCache, LayoutInterner, LayoutProblem, RawFunctionLayout,
|
LambdaName, LambdaSet, Layout, LayoutCache, LayoutProblem, RawFunctionLayout, STLayoutInterner,
|
||||||
STLayoutInterner, TagIdIntType, UnionLayout, WrappedVariant,
|
TagIdIntType, UnionLayout, WrappedVariant,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::{CollectIn, Vec};
|
use bumpalo::collections::{CollectIn, Vec};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
@ -1089,12 +1089,7 @@ impl<'a> Procs<'a> {
|
||||||
.raw_from_var(env.arena, annotation, env.subs)
|
.raw_from_var(env.arena, annotation, env.subs)
|
||||||
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
.unwrap_or_else(|err| panic!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||||
|
|
||||||
let top_level = ProcLayout::from_raw(
|
let top_level = ProcLayout::from_raw_named(env.arena, name, raw_layout);
|
||||||
env.arena,
|
|
||||||
&layout_cache.interner,
|
|
||||||
raw_layout,
|
|
||||||
name.captures_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// anonymous functions cannot reference themselves, therefore cannot be tail-recursive
|
// anonymous functions cannot reference themselves, therefore cannot be tail-recursive
|
||||||
// EXCEPT when the closure conversion makes it tail-recursive.
|
// EXCEPT when the closure conversion makes it tail-recursive.
|
||||||
|
@ -1169,16 +1164,8 @@ impl<'a> Procs<'a> {
|
||||||
let partial_proc_id = if let Some(partial_proc_id) =
|
let partial_proc_id = if let Some(partial_proc_id) =
|
||||||
self.partial_procs.symbol_to_id(name.name())
|
self.partial_procs.symbol_to_id(name.name())
|
||||||
{
|
{
|
||||||
let existing = self.partial_procs.get_id(partial_proc_id);
|
// NOTE we can't skip extra work! We still need to make the specialization for this
|
||||||
// if we're adding the same partial proc twice, they must be the actual same!
|
// invocation.
|
||||||
//
|
|
||||||
// NOTE we can't skip extra work! we still need to make the specialization for this
|
|
||||||
// invocation. The content of the `annotation` can be different, even if the variable
|
|
||||||
// number is the same
|
|
||||||
debug_assert_eq!(annotation, existing.annotation);
|
|
||||||
debug_assert_eq!(captured_symbols, existing.captured_symbols);
|
|
||||||
debug_assert_eq!(is_self_recursive, existing.is_self_recursive);
|
|
||||||
|
|
||||||
partial_proc_id
|
partial_proc_id
|
||||||
} else {
|
} else {
|
||||||
let pattern_symbols = pattern_symbols.into_bump_slice();
|
let pattern_symbols = pattern_symbols.into_bump_slice();
|
||||||
|
@ -1204,29 +1191,13 @@ impl<'a> Procs<'a> {
|
||||||
&[],
|
&[],
|
||||||
partial_proc_id,
|
partial_proc_id,
|
||||||
) {
|
) {
|
||||||
Ok((proc, _ignore_layout)) => {
|
Ok((proc, layout)) => {
|
||||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
let proc_name = proc.name;
|
||||||
// closure. We only specialize functions, storing this value with a closure
|
let function_layout =
|
||||||
// layout will give trouble.
|
ProcLayout::from_raw_named(env.arena, proc_name, layout);
|
||||||
let arguments = Vec::from_iter_in(
|
|
||||||
proc.args.iter().map(|(l, _)| *l),
|
|
||||||
env.arena,
|
|
||||||
)
|
|
||||||
.into_bump_slice();
|
|
||||||
|
|
||||||
let proper_layout = ProcLayout {
|
|
||||||
arguments,
|
|
||||||
result: proc.ret_layout,
|
|
||||||
captures_niche: proc.name.captures_niche(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
|
||||||
// need any closure argument. Here is where we correct this sort of thing,
|
|
||||||
// by trusting the layout of the Proc, not of what we specialize for
|
|
||||||
self.specialized.remove_specialized(name.name(), &layout);
|
|
||||||
self.specialized.insert_specialized(
|
self.specialized.insert_specialized(
|
||||||
name.name(),
|
proc_name.name(),
|
||||||
proper_layout,
|
function_layout,
|
||||||
proc,
|
proc,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1282,17 +1253,20 @@ impl<'a> Procs<'a> {
|
||||||
suspended.specialization(env.subs, name, layout, fn_var);
|
suspended.specialization(env.subs, name, layout, fn_var);
|
||||||
}
|
}
|
||||||
(PendingSpecializations::Making(_), false) => {
|
(PendingSpecializations::Making(_), false) => {
|
||||||
let symbol = name;
|
let proc_name = name;
|
||||||
|
|
||||||
let partial_proc_id = match self.partial_procs.symbol_to_id(symbol.name()) {
|
let partial_proc_id = match self.partial_procs.symbol_to_id(proc_name.name()) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => panic!("no partial_proc for {:?} in module {:?}", symbol, env.home),
|
None => panic!(
|
||||||
|
"no partial_proc for {:?} in module {:?}",
|
||||||
|
proc_name, env.home
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mark this proc as in-progress, so if we're dealing with
|
// Mark this proc as in-progress, so if we're dealing with
|
||||||
// mutually recursive functions, we don't loop forever.
|
// mutually recursive functions, we don't loop forever.
|
||||||
// (We had a bug around this before this system existed!)
|
// (We had a bug around this before this system existed!)
|
||||||
self.specialized.mark_in_progress(symbol.name(), layout);
|
self.specialized.mark_in_progress(proc_name.name(), layout);
|
||||||
|
|
||||||
// See https://github.com/roc-lang/roc/issues/1600
|
// See https://github.com/roc-lang/roc/issues/1600
|
||||||
//
|
//
|
||||||
|
@ -1301,39 +1275,21 @@ impl<'a> Procs<'a> {
|
||||||
//
|
//
|
||||||
// fn_var is the variable representing the type that we actually need for the
|
// fn_var is the variable representing the type that we actually need for the
|
||||||
// function right here.
|
// function right here.
|
||||||
//
|
|
||||||
// For some reason, it matters that we unify with the original variable. Extracting
|
|
||||||
// that variable into a SolvedType and then introducing it again severs some
|
|
||||||
// connection that turns out to be important
|
|
||||||
match specialize_variable(
|
match specialize_variable(
|
||||||
env,
|
env,
|
||||||
self,
|
self,
|
||||||
symbol,
|
proc_name,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
fn_var,
|
fn_var,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
partial_proc_id,
|
partial_proc_id,
|
||||||
) {
|
) {
|
||||||
Ok((proc, _ignore_layout)) => {
|
Ok((proc, raw_layout)) => {
|
||||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
let proc_layout =
|
||||||
// closure. We only specialize functions, storing this value with a closure
|
ProcLayout::from_raw_named(env.arena, proc_name, raw_layout);
|
||||||
// layout will give trouble.
|
|
||||||
let arguments =
|
|
||||||
Vec::from_iter_in(proc.args.iter().map(|(l, _)| *l), env.arena)
|
|
||||||
.into_bump_slice();
|
|
||||||
|
|
||||||
let proper_layout = ProcLayout {
|
|
||||||
arguments,
|
|
||||||
result: proc.ret_layout,
|
|
||||||
captures_niche: proc.name.captures_niche(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
|
||||||
// need any closure argument. Here is where we correct this sort of thing,
|
|
||||||
// by trusting the layout of the Proc, not of what we specialize for
|
|
||||||
self.specialized.remove_specialized(symbol.name(), &layout);
|
|
||||||
self.specialized
|
self.specialized
|
||||||
.insert_specialized(symbol.name(), proper_layout, proc);
|
.insert_specialized(proc_name.name(), proc_layout, proc);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
panic!("TODO generate a RuntimeError message for {:?}", error);
|
panic!("TODO generate a RuntimeError message for {:?}", error);
|
||||||
|
@ -2463,17 +2419,6 @@ fn from_can_let<'a>(
|
||||||
|
|
||||||
lower_rest!(variable, cont.value)
|
lower_rest!(variable, cont.value)
|
||||||
}
|
}
|
||||||
Accessor(accessor_data) => {
|
|
||||||
let fresh_record_symbol = env.unique_symbol();
|
|
||||||
register_noncapturing_closure(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
*symbol,
|
|
||||||
accessor_data.to_closure_data(fresh_record_symbol),
|
|
||||||
);
|
|
||||||
|
|
||||||
lower_rest!(variable, cont.value)
|
|
||||||
}
|
|
||||||
Var(original, _) | AbilityMember(original, _, _)
|
Var(original, _) | AbilityMember(original, _, _)
|
||||||
if procs.get_partial_proc(original).is_none() =>
|
if procs.get_partial_proc(original).is_none() =>
|
||||||
{
|
{
|
||||||
|
@ -3040,55 +2985,18 @@ fn specialize_suspended<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
match specialize_variable(env, procs, name, layout_cache, var, &[], partial_proc) {
|
match specialize_variable(env, procs, name, layout_cache, var, &[], partial_proc) {
|
||||||
Ok((proc, _layout)) => {
|
Ok((proc, raw_layout)) => {
|
||||||
// TODO this code is duplicated elsewhere
|
let proc_layout = ProcLayout::from_raw_named(env.arena, name, raw_layout);
|
||||||
|
|
||||||
// the `layout` is a function pointer, while `_ignore_layout` can be a
|
|
||||||
// closure. We only specialize functions, storing this value with a closure
|
|
||||||
// layout will give trouble.
|
|
||||||
let arguments = Vec::from_iter_in(proc.args.iter().map(|(l, _)| *l), env.arena)
|
|
||||||
.into_bump_slice();
|
|
||||||
|
|
||||||
let proper_layout = ProcLayout {
|
|
||||||
arguments,
|
|
||||||
result: proc.ret_layout,
|
|
||||||
captures_niche: proc.name.captures_niche(),
|
|
||||||
};
|
|
||||||
if procs.is_module_thunk(proc.name.name()) {
|
|
||||||
debug_assert!(
|
|
||||||
proper_layout.arguments.is_empty(),
|
|
||||||
"{:?} from {:?}",
|
|
||||||
name,
|
|
||||||
proper_layout
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: some functions are specialized to have a closure, but don't actually
|
|
||||||
// need any closure argument. Here is where we correct this sort of thing,
|
|
||||||
// by trusting the layout of the Proc, not of what we specialize for
|
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
.remove_specialized(name.name(), &outside_layout);
|
.insert_specialized(name.name(), proc_layout, proc);
|
||||||
procs
|
|
||||||
.specialized
|
|
||||||
.insert_specialized(name.name(), proper_layout, proc);
|
|
||||||
}
|
}
|
||||||
Err(SpecializeFailure {
|
Err(SpecializeFailure {
|
||||||
attempted_layout, ..
|
attempted_layout, ..
|
||||||
}) => {
|
}) => {
|
||||||
let proc = generate_runtime_error_function(
|
let proc = generate_runtime_error_function(env, name, attempted_layout);
|
||||||
env,
|
|
||||||
layout_cache,
|
|
||||||
name.name(),
|
|
||||||
attempted_layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
let top_level = ProcLayout::from_raw(
|
let top_level = ProcLayout::from_raw_named(env.arena, name, attempted_layout);
|
||||||
env.arena,
|
|
||||||
&layout_cache.interner,
|
|
||||||
attempted_layout,
|
|
||||||
CapturesNiche::no_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
|
@ -3234,12 +3142,7 @@ fn specialize_external_help<'a>(
|
||||||
|
|
||||||
match specialization_result {
|
match specialization_result {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
let top_level = ProcLayout::from_raw(
|
let top_level = ProcLayout::from_raw_named(env.arena, name, layout);
|
||||||
env.arena,
|
|
||||||
&layout_cache.interner,
|
|
||||||
layout,
|
|
||||||
proc.name.captures_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if procs.is_module_thunk(name.name()) {
|
if procs.is_module_thunk(name.name()) {
|
||||||
debug_assert!(top_level.arguments.is_empty());
|
debug_assert!(top_level.arguments.is_empty());
|
||||||
|
@ -3250,15 +3153,9 @@ fn specialize_external_help<'a>(
|
||||||
.insert_specialized(name.name(), top_level, proc);
|
.insert_specialized(name.name(), top_level, proc);
|
||||||
}
|
}
|
||||||
Err(SpecializeFailure { attempted_layout }) => {
|
Err(SpecializeFailure { attempted_layout }) => {
|
||||||
let proc =
|
let proc = generate_runtime_error_function(env, name, attempted_layout);
|
||||||
generate_runtime_error_function(env, layout_cache, name.name(), attempted_layout);
|
|
||||||
|
|
||||||
let top_level = ProcLayout::from_raw(
|
let top_level = ProcLayout::from_raw_named(env.arena, name, attempted_layout);
|
||||||
env.arena,
|
|
||||||
&layout_cache.interner,
|
|
||||||
attempted_layout,
|
|
||||||
proc.name.captures_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
|
@ -3269,8 +3166,7 @@ fn specialize_external_help<'a>(
|
||||||
|
|
||||||
fn generate_runtime_error_function<'a>(
|
fn generate_runtime_error_function<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
layout_cache: &LayoutCache<'a>,
|
lambda_name: LambdaName<'a>,
|
||||||
name: Symbol,
|
|
||||||
layout: RawFunctionLayout<'a>,
|
layout: RawFunctionLayout<'a>,
|
||||||
) -> Proc<'a> {
|
) -> Proc<'a> {
|
||||||
let mut msg = bumpalo::collections::string::String::with_capacity_in(80, env.arena);
|
let mut msg = bumpalo::collections::string::String::with_capacity_in(80, env.arena);
|
||||||
|
@ -3278,7 +3174,7 @@ fn generate_runtime_error_function<'a>(
|
||||||
write!(
|
write!(
|
||||||
&mut msg,
|
&mut msg,
|
||||||
"The {:?} function could not be generated, likely due to a type error.",
|
"The {:?} function could not be generated, likely due to a type error.",
|
||||||
name
|
lambda_name.name(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -3294,7 +3190,7 @@ fn generate_runtime_error_function<'a>(
|
||||||
let (args, ret_layout) = match layout {
|
let (args, ret_layout) = match layout {
|
||||||
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
RawFunctionLayout::Function(arg_layouts, lambda_set, ret_layout) => {
|
||||||
let real_arg_layouts =
|
let real_arg_layouts =
|
||||||
lambda_set.extend_argument_list(env.arena, &layout_cache.interner, arg_layouts);
|
lambda_set.extend_argument_list_for_named(env.arena, lambda_name, arg_layouts);
|
||||||
let mut args = Vec::with_capacity_in(real_arg_layouts.len(), env.arena);
|
let mut args = Vec::with_capacity_in(real_arg_layouts.len(), env.arena);
|
||||||
|
|
||||||
for arg in arg_layouts {
|
for arg in arg_layouts {
|
||||||
|
@ -3310,7 +3206,7 @@ fn generate_runtime_error_function<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
Proc {
|
Proc {
|
||||||
name: LambdaName::no_niche(name),
|
name: lambda_name,
|
||||||
args,
|
args,
|
||||||
body: runtime_error,
|
body: runtime_error,
|
||||||
closure_data_layout: None,
|
closure_data_layout: None,
|
||||||
|
@ -3508,8 +3404,9 @@ fn specialize_proc_help<'a>(
|
||||||
let hole = env.arena.alloc(Stmt::Ret(assigned));
|
let hole = env.arena.alloc(Stmt::Ret(assigned));
|
||||||
let forced = force_thunk(env, lambda_name.name(), result, assigned, hole);
|
let forced = force_thunk(env, lambda_name.name(), result, assigned, hole);
|
||||||
|
|
||||||
|
let lambda_name = LambdaName::no_niche(name);
|
||||||
let proc = Proc {
|
let proc = Proc {
|
||||||
name: LambdaName::no_niche(name),
|
name: lambda_name,
|
||||||
args: &[],
|
args: &[],
|
||||||
body: forced,
|
body: forced,
|
||||||
closure_data_layout: None,
|
closure_data_layout: None,
|
||||||
|
@ -3519,12 +3416,7 @@ fn specialize_proc_help<'a>(
|
||||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||||
};
|
};
|
||||||
|
|
||||||
let top_level = ProcLayout::from_raw(
|
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, layout);
|
||||||
env.arena,
|
|
||||||
&layout_cache.interner,
|
|
||||||
layout,
|
|
||||||
CapturesNiche::no_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.specialized.insert_specialized(name, top_level, proc);
|
procs.specialized.insert_specialized(name, top_level, proc);
|
||||||
|
|
||||||
|
@ -4062,7 +3954,7 @@ pub struct ProcLayout<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ProcLayout<'a> {
|
impl<'a> ProcLayout<'a> {
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
old_arguments: &'a [Layout<'a>],
|
old_arguments: &'a [Layout<'a>],
|
||||||
old_captures_niche: CapturesNiche<'a>,
|
old_captures_niche: CapturesNiche<'a>,
|
||||||
|
@ -4085,16 +3977,16 @@ impl<'a> ProcLayout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_raw(
|
fn from_raw_named(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
interner: &LayoutInterner<'a>,
|
lambda_name: LambdaName<'a>,
|
||||||
raw: RawFunctionLayout<'a>,
|
raw: RawFunctionLayout<'a>,
|
||||||
captures_niche: CapturesNiche<'a>,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match raw {
|
match raw {
|
||||||
RawFunctionLayout::Function(arguments, lambda_set, result) => {
|
RawFunctionLayout::Function(arguments, lambda_set, result) => {
|
||||||
let arguments = lambda_set.extend_argument_list(arena, interner, arguments);
|
let arguments =
|
||||||
ProcLayout::new(arena, arguments, captures_niche, *result)
|
lambda_set.extend_argument_list_for_named(arena, lambda_name, arguments);
|
||||||
|
ProcLayout::new(arena, arguments, lambda_name.captures_niche(), *result)
|
||||||
}
|
}
|
||||||
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
RawFunctionLayout::ZeroArgumentThunk(result) => {
|
||||||
ProcLayout::new(arena, &[], CapturesNiche::no_niche(), result)
|
ProcLayout::new(arena, &[], CapturesNiche::no_niche(), result)
|
||||||
|
@ -5567,15 +5459,8 @@ pub fn with_hole<'a>(
|
||||||
"match_on_closure_argument"
|
"match_on_closure_argument"
|
||||||
);
|
);
|
||||||
|
|
||||||
// NB: I don't think the top_level here can have a captures niche?
|
|
||||||
let top_level_capture_niche = CapturesNiche::no_niche();
|
|
||||||
let top_level = ProcLayout::from_raw(env.arena, &layout_cache.interner, closure_data_layout, top_level_capture_niche);
|
|
||||||
|
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
|
||||||
let arg_layouts = top_level.arguments;
|
|
||||||
let ret_layout = top_level.result;
|
|
||||||
|
|
||||||
match closure_data_layout {
|
match closure_data_layout {
|
||||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||||
lowlevel_match_on_lambda_set(
|
lowlevel_match_on_lambda_set(
|
||||||
|
@ -5584,9 +5469,14 @@ pub fn with_hole<'a>(
|
||||||
lambda_set,
|
lambda_set,
|
||||||
op,
|
op,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
|(top_level_function, closure_data, closure_env_layout, specialization_id, update_mode)| {
|
|(lambda_name, closure_data, closure_env_layout, specialization_id, update_mode)| {
|
||||||
|
// Build a call for a specific lambda in the set
|
||||||
|
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, closure_data_layout);
|
||||||
|
let arg_layouts = top_level.arguments;
|
||||||
|
let ret_layout = top_level.result;
|
||||||
|
|
||||||
let passed_function = PassedFunction {
|
let passed_function = PassedFunction {
|
||||||
name: top_level_function,
|
name: lambda_name,
|
||||||
captured_environment: closure_data_symbol,
|
captured_environment: closure_data_symbol,
|
||||||
owns_captured_environment: false,
|
owns_captured_environment: false,
|
||||||
specialization_id,
|
specialization_id,
|
||||||
|
@ -5594,7 +5484,6 @@ pub fn with_hole<'a>(
|
||||||
return_layout: ret_layout,
|
return_layout: ret_layout,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
let higher_order = HigherOrderLowLevel {
|
let higher_order = HigherOrderLowLevel {
|
||||||
op: crate::low_level::HigherOrder::$ho { $($x,)* },
|
op: crate::low_level::HigherOrder::$ho { $($x,)* },
|
||||||
closure_env_layout,
|
closure_env_layout,
|
||||||
|
@ -5604,7 +5493,7 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
self::Call {
|
self::Call {
|
||||||
call_type: CallType::HigherOrder(arena.alloc(higher_order)),
|
call_type: CallType::HigherOrder(arena.alloc(higher_order)),
|
||||||
arguments: arena.alloc([$($x,)* top_level_function.name(), closure_data]),
|
arguments: arena.alloc([$($x,)* lambda_name.name(), closure_data]),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
layout,
|
layout,
|
||||||
|
@ -7079,10 +6968,18 @@ fn substitute_in_stmt_help<'a>(
|
||||||
default_branch,
|
default_branch,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
} => {
|
} => {
|
||||||
let opt_default = substitute_in_stmt_help(arena, default_branch.1, subs);
|
|
||||||
|
|
||||||
let mut did_change = false;
|
let mut did_change = false;
|
||||||
|
|
||||||
|
let cond_symbol = match substitute(subs, *cond_symbol) {
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
None => *cond_symbol,
|
||||||
|
};
|
||||||
|
|
||||||
|
let opt_default = substitute_in_stmt_help(arena, default_branch.1, subs);
|
||||||
|
|
||||||
let opt_branches = Vec::from_iter_in(
|
let opt_branches = Vec::from_iter_in(
|
||||||
branches.iter().map(|(label, info, branch)| {
|
branches.iter().map(|(label, info, branch)| {
|
||||||
match substitute_in_stmt_help(arena, branch, subs) {
|
match substitute_in_stmt_help(arena, branch, subs) {
|
||||||
|
@ -7119,7 +7016,7 @@ fn substitute_in_stmt_help<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(arena.alloc(Switch {
|
Some(arena.alloc(Switch {
|
||||||
cond_symbol: *cond_symbol,
|
cond_symbol,
|
||||||
cond_layout: *cond_layout,
|
cond_layout: *cond_layout,
|
||||||
default_branch,
|
default_branch,
|
||||||
branches,
|
branches,
|
||||||
|
@ -8326,12 +8223,8 @@ fn specialize_symbol<'a>(
|
||||||
"imported functions are top-level and should never capture"
|
"imported functions are top-level and should never capture"
|
||||||
);
|
);
|
||||||
|
|
||||||
let function_ptr_layout = ProcLayout::from_raw(
|
let function_ptr_layout =
|
||||||
env.arena,
|
ProcLayout::from_raw_named(env.arena, lambda_name, raw);
|
||||||
&layout_cache.interner,
|
|
||||||
raw,
|
|
||||||
lambda_name.captures_niche(),
|
|
||||||
);
|
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
arg_var,
|
arg_var,
|
||||||
|
@ -8362,17 +8255,13 @@ fn specialize_symbol<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||||
let top_level = ProcLayout::from_raw(
|
let lambda_name = LambdaName::no_niche(original);
|
||||||
env.arena,
|
let top_level = ProcLayout::from_raw_named(env.arena, lambda_name, raw);
|
||||||
&layout_cache.interner,
|
|
||||||
raw,
|
|
||||||
CapturesNiche::no_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
arg_var,
|
arg_var,
|
||||||
LambdaName::no_niche(original),
|
lambda_name,
|
||||||
top_level,
|
top_level,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
);
|
);
|
||||||
|
@ -8439,12 +8328,8 @@ fn specialize_symbol<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// define the function pointer
|
// define the function pointer
|
||||||
let function_ptr_layout = ProcLayout::from_raw(
|
let function_ptr_layout =
|
||||||
env.arena,
|
ProcLayout::from_raw_named(env.arena, lambda_name, res_layout);
|
||||||
&layout_cache.interner,
|
|
||||||
res_layout,
|
|
||||||
lambda_name.captures_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// this is a closure by capture, meaning it itself captures local variables.
|
// this is a closure by capture, meaning it itself captures local variables.
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
|
@ -8494,12 +8379,8 @@ fn specialize_symbol<'a>(
|
||||||
debug_assert!(lambda_name.no_captures());
|
debug_assert!(lambda_name.no_captures());
|
||||||
|
|
||||||
// define the function pointer
|
// define the function pointer
|
||||||
let function_ptr_layout = ProcLayout::from_raw(
|
let function_ptr_layout =
|
||||||
env.arena,
|
ProcLayout::from_raw_named(env.arena, lambda_name, res_layout);
|
||||||
&layout_cache.interner,
|
|
||||||
res_layout,
|
|
||||||
lambda_name.captures_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.insert_passed_by_name(
|
procs.insert_passed_by_name(
|
||||||
env,
|
env,
|
||||||
|
@ -8851,7 +8732,7 @@ fn call_by_name_help<'a>(
|
||||||
// number of arguments actually passed.
|
// number of arguments actually passed.
|
||||||
let top_level_layout = {
|
let top_level_layout = {
|
||||||
let argument_layouts =
|
let argument_layouts =
|
||||||
lambda_set.extend_argument_list(env.arena, &layout_cache.interner, argument_layouts);
|
lambda_set.extend_argument_list_for_named(env.arena, proc_name, argument_layouts);
|
||||||
ProcLayout::new(
|
ProcLayout::new(
|
||||||
env.arena,
|
env.arena,
|
||||||
argument_layouts,
|
argument_layouts,
|
||||||
|
@ -9033,12 +8914,8 @@ fn call_by_name_help<'a>(
|
||||||
) {
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
let proc_name = proc.name;
|
let proc_name = proc.name;
|
||||||
let function_layout = ProcLayout::from_raw(
|
let function_layout =
|
||||||
env.arena,
|
ProcLayout::from_raw_named(env.arena, proc_name, layout);
|
||||||
&layout_cache.interner,
|
|
||||||
layout,
|
|
||||||
proc_name.captures_niche(),
|
|
||||||
);
|
|
||||||
procs.specialized.insert_specialized(
|
procs.specialized.insert_specialized(
|
||||||
proc_name.name(),
|
proc_name.name(),
|
||||||
function_layout,
|
function_layout,
|
||||||
|
@ -9063,17 +8940,15 @@ fn call_by_name_help<'a>(
|
||||||
Err(SpecializeFailure { attempted_layout }) => {
|
Err(SpecializeFailure { attempted_layout }) => {
|
||||||
let proc = generate_runtime_error_function(
|
let proc = generate_runtime_error_function(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
proc_name,
|
||||||
proc_name.name(),
|
|
||||||
attempted_layout,
|
attempted_layout,
|
||||||
);
|
);
|
||||||
|
|
||||||
let proc_name = proc.name;
|
let proc_name = proc.name;
|
||||||
let function_layout = ProcLayout::from_raw(
|
let function_layout = ProcLayout::from_raw_named(
|
||||||
env.arena,
|
env.arena,
|
||||||
&layout_cache.interner,
|
proc_name,
|
||||||
attempted_layout,
|
attempted_layout,
|
||||||
proc_name.captures_niche(),
|
|
||||||
);
|
);
|
||||||
procs.specialized.insert_specialized(
|
procs.specialized.insert_specialized(
|
||||||
proc_name.name(),
|
proc_name.name(),
|
||||||
|
@ -9208,8 +9083,7 @@ fn call_by_name_module_thunk<'a>(
|
||||||
Err(SpecializeFailure { attempted_layout }) => {
|
Err(SpecializeFailure { attempted_layout }) => {
|
||||||
let proc = generate_runtime_error_function(
|
let proc = generate_runtime_error_function(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
LambdaName::no_niche(proc_name),
|
||||||
proc_name,
|
|
||||||
attempted_layout,
|
attempted_layout,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10167,15 +10041,6 @@ fn from_can_pattern_help<'a>(
|
||||||
// it must be an optional field, and we will use the default
|
// it must be an optional field, and we will use the default
|
||||||
match &destruct.value.typ {
|
match &destruct.value.typ {
|
||||||
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
|
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
|
||||||
// TODO these don't match up in the uniqueness inference; when we remove
|
|
||||||
// that, reinstate this assert!
|
|
||||||
//
|
|
||||||
// dbg!(&env.subs.get_content_without_compacting(*field_var));
|
|
||||||
// dbg!(&env.subs.get_content_without_compacting(destruct.var).content);
|
|
||||||
// debug_assert_eq!(
|
|
||||||
// env.subs.get_root_key_without_compacting(*field_var),
|
|
||||||
// env.subs.get_root_key_without_compacting(destruct.value.var)
|
|
||||||
// );
|
|
||||||
assignments.push((
|
assignments.push((
|
||||||
destruct.value.symbol,
|
destruct.value.symbol,
|
||||||
// destruct.value.var,
|
// destruct.value.var,
|
||||||
|
@ -10574,7 +10439,6 @@ fn match_on_lambda_set<'a>(
|
||||||
|
|
||||||
let result = union_lambda_set_to_switch(
|
let result = union_lambda_set_to_switch(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
lambda_set,
|
lambda_set,
|
||||||
closure_tag_id_symbol,
|
closure_tag_id_symbol,
|
||||||
union_layout.tag_id_layout(),
|
union_layout.tag_id_layout(),
|
||||||
|
@ -10609,19 +10473,16 @@ fn match_on_lambda_set<'a>(
|
||||||
// Lambda set is empty, so this function is never called; synthesize a function
|
// Lambda set is empty, so this function is never called; synthesize a function
|
||||||
// that always yields a runtime error.
|
// that always yields a runtime error.
|
||||||
let name = env.unique_symbol();
|
let name = env.unique_symbol();
|
||||||
|
let lambda_name = LambdaName::no_niche(name);
|
||||||
let function_layout =
|
let function_layout =
|
||||||
RawFunctionLayout::Function(argument_layouts, lambda_set, return_layout);
|
RawFunctionLayout::Function(argument_layouts, lambda_set, return_layout);
|
||||||
let proc =
|
let proc = generate_runtime_error_function(env, lambda_name, function_layout);
|
||||||
generate_runtime_error_function(env, layout_cache, name, function_layout);
|
let top_level =
|
||||||
let top_level = ProcLayout::from_raw(
|
ProcLayout::from_raw_named(env.arena, lambda_name, function_layout);
|
||||||
env.arena,
|
|
||||||
&layout_cache.interner,
|
|
||||||
function_layout,
|
|
||||||
CapturesNiche::no_niche(),
|
|
||||||
);
|
|
||||||
|
|
||||||
procs.specialized.insert_specialized(name, top_level, proc);
|
procs.specialized.insert_specialized(name, top_level, proc);
|
||||||
LambdaName::no_niche(name)
|
|
||||||
|
lambda_name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10635,7 +10496,6 @@ fn match_on_lambda_set<'a>(
|
||||||
|
|
||||||
union_lambda_set_branch_help(
|
union_lambda_set_branch_help(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
function_symbol,
|
function_symbol,
|
||||||
closure_info,
|
closure_info,
|
||||||
argument_symbols,
|
argument_symbols,
|
||||||
|
@ -10658,7 +10518,6 @@ fn match_on_lambda_set<'a>(
|
||||||
|
|
||||||
union_lambda_set_branch_help(
|
union_lambda_set_branch_help(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
function_symbol,
|
function_symbol,
|
||||||
closure_info,
|
closure_info,
|
||||||
argument_symbols,
|
argument_symbols,
|
||||||
|
@ -10706,7 +10565,6 @@ fn match_on_lambda_set<'a>(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn union_lambda_set_to_switch<'a>(
|
fn union_lambda_set_to_switch<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
layout_cache: &LayoutCache<'a>,
|
|
||||||
lambda_set: LambdaSet<'a>,
|
lambda_set: LambdaSet<'a>,
|
||||||
closure_tag_id_symbol: Symbol,
|
closure_tag_id_symbol: Symbol,
|
||||||
closure_tag_id_layout: Layout<'a>,
|
closure_tag_id_layout: Layout<'a>,
|
||||||
|
@ -10741,7 +10599,6 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
|
|
||||||
let stmt = union_lambda_set_branch(
|
let stmt = union_lambda_set_branch(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
join_point_id,
|
join_point_id,
|
||||||
lambda_name,
|
lambda_name,
|
||||||
closure_info,
|
closure_info,
|
||||||
|
@ -10783,7 +10640,6 @@ fn union_lambda_set_to_switch<'a>(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn union_lambda_set_branch<'a>(
|
fn union_lambda_set_branch<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
layout_cache: &LayoutCache<'a>,
|
|
||||||
join_point_id: JoinPointId,
|
join_point_id: JoinPointId,
|
||||||
lambda_name: LambdaName<'a>,
|
lambda_name: LambdaName<'a>,
|
||||||
closure_info: ClosureInfo<'a>,
|
closure_info: ClosureInfo<'a>,
|
||||||
|
@ -10797,7 +10653,6 @@ fn union_lambda_set_branch<'a>(
|
||||||
|
|
||||||
union_lambda_set_branch_help(
|
union_lambda_set_branch_help(
|
||||||
env,
|
env,
|
||||||
layout_cache,
|
|
||||||
lambda_name,
|
lambda_name,
|
||||||
closure_info,
|
closure_info,
|
||||||
argument_symbols_slice,
|
argument_symbols_slice,
|
||||||
|
@ -10821,7 +10676,6 @@ enum ClosureInfo<'a> {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn union_lambda_set_branch_help<'a>(
|
fn union_lambda_set_branch_help<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
layout_cache: &LayoutCache<'a>,
|
|
||||||
lambda_name: LambdaName<'a>,
|
lambda_name: LambdaName<'a>,
|
||||||
closure_info: ClosureInfo<'a>,
|
closure_info: ClosureInfo<'a>,
|
||||||
argument_symbols_slice: &'a [Symbol],
|
argument_symbols_slice: &'a [Symbol],
|
||||||
|
@ -10835,22 +10689,21 @@ fn union_lambda_set_branch_help<'a>(
|
||||||
lambda_set,
|
lambda_set,
|
||||||
closure_data_symbol,
|
closure_data_symbol,
|
||||||
} => {
|
} => {
|
||||||
let argument_layouts = lambda_set.extend_argument_list(
|
let argument_layouts = lambda_set.extend_argument_list_for_named(
|
||||||
env.arena,
|
env.arena,
|
||||||
&layout_cache.interner,
|
lambda_name,
|
||||||
argument_layouts_slice,
|
argument_layouts_slice,
|
||||||
);
|
);
|
||||||
let argument_symbols = if argument_layouts.len() > argument_layouts_slice.len() {
|
|
||||||
// extend symbols with the symbol of the closure environment
|
// Since this lambda captures, the arguments must have been extended.
|
||||||
let mut argument_symbols =
|
debug_assert!(argument_layouts.len() > argument_layouts_slice.len());
|
||||||
Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena);
|
// Extend symbols with the symbol of the closure environment.
|
||||||
argument_symbols.extend(argument_symbols_slice);
|
let mut argument_symbols =
|
||||||
argument_symbols.push(closure_data_symbol);
|
Vec::with_capacity_in(argument_symbols_slice.len() + 1, env.arena);
|
||||||
argument_symbols.into_bump_slice()
|
argument_symbols.extend(argument_symbols_slice);
|
||||||
} else {
|
argument_symbols.push(closure_data_symbol);
|
||||||
argument_symbols_slice
|
|
||||||
};
|
(argument_layouts, argument_symbols.into_bump_slice())
|
||||||
(argument_layouts, argument_symbols)
|
|
||||||
}
|
}
|
||||||
ClosureInfo::DoesNotCapture => {
|
ClosureInfo::DoesNotCapture => {
|
||||||
// sometimes unification causes a function that does not itself capture anything
|
// sometimes unification causes a function that does not itself capture anything
|
||||||
|
|
|
@ -1254,9 +1254,9 @@ impl<'a> LambdaName<'a> {
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct LambdaSet<'a> {
|
pub struct LambdaSet<'a> {
|
||||||
/// collection of function names and their closure arguments
|
/// collection of function names and their closure arguments
|
||||||
set: &'a [(Symbol, &'a [Layout<'a>])],
|
pub(crate) set: &'a [(Symbol, &'a [Layout<'a>])],
|
||||||
/// how the closure will be represented at runtime
|
/// how the closure will be represented at runtime
|
||||||
representation: Interned<Layout<'a>>,
|
pub(crate) representation: Interned<Layout<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1610,37 +1610,31 @@ impl<'a> LambdaSet<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extend_argument_list<I>(
|
/// If `lambda_name` captures, extend the arguments to the lambda with the lambda set, from
|
||||||
|
/// which the lambda should extract its captures from.
|
||||||
|
///
|
||||||
|
/// If `lambda_name` doesn't capture, the arguments are unaffected.
|
||||||
|
pub(crate) fn extend_argument_list_for_named(
|
||||||
&self,
|
&self,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
interner: &I,
|
lambda_name: LambdaName<'a>,
|
||||||
argument_layouts: &'a [Layout<'a>],
|
argument_layouts: &'a [Layout<'a>],
|
||||||
) -> &'a [Layout<'a>]
|
) -> &'a [Layout<'a>] {
|
||||||
where
|
debug_assert!(
|
||||||
I: Interner<'a, Layout<'a>>,
|
self.set
|
||||||
{
|
.contains(&(lambda_name.name, lambda_name.captures_niche.0)),
|
||||||
match self.call_by_name_options(interner) {
|
"{:?}",
|
||||||
ClosureCallOptions::Void => argument_layouts,
|
(self, lambda_name)
|
||||||
ClosureCallOptions::Struct {
|
);
|
||||||
field_layouts: &[], ..
|
// If we don't capture, there is nothing to extend.
|
||||||
} => {
|
if lambda_name.captures_niche.0.is_empty() {
|
||||||
// this function does not have anything in its closure, and the lambda set is a
|
argument_layouts
|
||||||
// singleton, so we pass no extra argument
|
} else {
|
||||||
argument_layouts
|
let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena);
|
||||||
}
|
arguments.extend(argument_layouts);
|
||||||
ClosureCallOptions::Struct { .. }
|
arguments.push(Layout::LambdaSet(*self));
|
||||||
| ClosureCallOptions::Union(_)
|
|
||||||
| ClosureCallOptions::UnwrappedCapture(_) => {
|
|
||||||
let mut arguments = Vec::with_capacity_in(argument_layouts.len() + 1, arena);
|
|
||||||
arguments.extend(argument_layouts);
|
|
||||||
arguments.push(Layout::LambdaSet(*self));
|
|
||||||
|
|
||||||
arguments.into_bump_slice()
|
arguments.into_bump_slice()
|
||||||
}
|
|
||||||
ClosureCallOptions::EnumDispatch(_) => {
|
|
||||||
// No captures, don't pass this along
|
|
||||||
argument_layouts
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
3
crates/compiler/parse/fuzz/.gitignore
vendored
3
crates/compiler/parse/fuzz/.gitignore
vendored
|
@ -1,3 +0,0 @@
|
||||||
target
|
|
||||||
corpus
|
|
||||||
artifacts
|
|
424
crates/compiler/parse/fuzz/Cargo.lock
generated
424
crates/compiler/parse/fuzz/Cargo.lock
generated
|
@ -1,424 +0,0 @@
|
||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.7.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arbitrary"
|
|
||||||
version = "0.4.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backtrace"
|
|
||||||
version = "0.3.66"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
"object",
|
|
||||||
"rustc-demangle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitmaps"
|
|
||||||
version = "2.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
|
|
||||||
dependencies = [
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitvec"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
|
||||||
dependencies = [
|
|
||||||
"funty",
|
|
||||||
"radium",
|
|
||||||
"tap",
|
|
||||||
"wyz",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.0.73"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "doc-comment"
|
|
||||||
version = "0.3.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encode_unicode"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fnv"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "funty"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.26.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.12.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
"bumpalo",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heck"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "im"
|
|
||||||
version = "15.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9"
|
|
||||||
dependencies = [
|
|
||||||
"bitmaps",
|
|
||||||
"rand_core",
|
|
||||||
"rand_xoshiro",
|
|
||||||
"sized-chunks",
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "im-rc"
|
|
||||||
version = "15.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe"
|
|
||||||
dependencies = [
|
|
||||||
"bitmaps",
|
|
||||||
"rand_core",
|
|
||||||
"rand_xoshiro",
|
|
||||||
"sized-chunks",
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.131"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libfuzzer-sys"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee8c42ab62f43795ed77a965ed07994c5584cdc94fd0ebf14b22ac1524077acc"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.29.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.43"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "radium"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.6.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_xoshiro"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_collections"
|
|
||||||
version = "0.0.1"
|
|
||||||
dependencies = [
|
|
||||||
"bitvec",
|
|
||||||
"bumpalo",
|
|
||||||
"fnv",
|
|
||||||
"hashbrown",
|
|
||||||
"im",
|
|
||||||
"im-rc",
|
|
||||||
"wyhash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_error_macros"
|
|
||||||
version = "0.0.1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_ident"
|
|
||||||
version = "0.0.1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_module"
|
|
||||||
version = "0.0.1"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"roc_collections",
|
|
||||||
"roc_error_macros",
|
|
||||||
"roc_ident",
|
|
||||||
"roc_region",
|
|
||||||
"snafu",
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_parse"
|
|
||||||
version = "0.0.1"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"encode_unicode",
|
|
||||||
"roc_collections",
|
|
||||||
"roc_module",
|
|
||||||
"roc_region",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_parse-fuzz"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"libfuzzer-sys",
|
|
||||||
"roc_parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_region"
|
|
||||||
version = "0.0.1"
|
|
||||||
dependencies = [
|
|
||||||
"static_assertions",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-demangle"
|
|
||||||
version = "0.1.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sized-chunks"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
|
|
||||||
dependencies = [
|
|
||||||
"bitmaps",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "snafu"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"doc-comment",
|
|
||||||
"snafu-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "snafu-derive"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_assertions"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.99"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tap"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wyhash"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf6e163c25e3fac820b4b453185ea2dea3b6a3e0a721d4d23d75bd33734c295"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wyz"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e"
|
|
||||||
dependencies = [
|
|
||||||
"tap",
|
|
||||||
]
|
|
|
@ -1,13 +0,0 @@
|
||||||
# fuzz
|
|
||||||
|
|
||||||
To setup fuzzing you will need to install cargo-fuzz and run with rust nightly:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ cargo install cargo-fuzz
|
|
||||||
$ cargo +nightly fuzz run -j<cores> <target> -- -dict=dict.txt
|
|
||||||
```
|
|
||||||
|
|
||||||
The different targets can be found by running `cargo fuzz list`.
|
|
||||||
|
|
||||||
When a bug is found, it will be reported with commands to run it again and look for a minimized version.
|
|
||||||
If you are going to file a bug, please minimize the input before filing the bug.
|
|
|
@ -1,11 +0,0 @@
|
||||||
#![no_main]
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
|
||||||
use roc_parse::test_helpers::parse_header_with;
|
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
|
||||||
if let Ok(input) = std::str::from_utf8(data) {
|
|
||||||
let arena = Bump::new();
|
|
||||||
let _actual = parse_header_with(&arena, input.trim());
|
|
||||||
}
|
|
||||||
});
|
|
384
crates/compiler/parse/generator.roc
Normal file
384
crates/compiler/parse/generator.roc
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
app "generator"
|
||||||
|
packages {
|
||||||
|
pf: "https://github.com/roc-lang/basic-cli/releases/download/0.1.3/5SXwdW7rH8QAOnD71IkHcFxCmBEPtFSLAIkclPEgjHQ.tar.br",
|
||||||
|
}
|
||||||
|
imports [
|
||||||
|
pf.Stdout,
|
||||||
|
pf.File,
|
||||||
|
pf.Path,
|
||||||
|
pf.Task.{ Task },
|
||||||
|
]
|
||||||
|
|
||||||
|
provides [main] to pf
|
||||||
|
|
||||||
|
RenderTree : [
|
||||||
|
Text Str,
|
||||||
|
Items (List RenderTree),
|
||||||
|
Line (List RenderTree),
|
||||||
|
Indent (List RenderTree),
|
||||||
|
Import {modu: List Str, name: Str},
|
||||||
|
]
|
||||||
|
|
||||||
|
renderFile : RenderTree -> Str
|
||||||
|
renderFile = \tree ->
|
||||||
|
render (Items [
|
||||||
|
formatImports (findImports tree),
|
||||||
|
tree
|
||||||
|
])
|
||||||
|
|
||||||
|
findImports : RenderTree -> Set { modu: (List Str), name: Str }
|
||||||
|
findImports = \tree ->
|
||||||
|
when tree is
|
||||||
|
Text _ -> Set.empty
|
||||||
|
Items list | Indent list | Line list ->
|
||||||
|
List.walk list Set.empty \acc, item -> Set.union acc (findImports item)
|
||||||
|
Import import -> Set.single import
|
||||||
|
|
||||||
|
formatImports : Set { modu: (List Str), name: Str } -> RenderTree
|
||||||
|
formatImports = \set ->
|
||||||
|
if hasDupImports set then
|
||||||
|
crash "Duplicate imports!"
|
||||||
|
else
|
||||||
|
Items (
|
||||||
|
set
|
||||||
|
|> Set.toList
|
||||||
|
# TODO: Sort by module name
|
||||||
|
|> List.map \{ modu, name } ->
|
||||||
|
Line [
|
||||||
|
Text "use ",
|
||||||
|
Text (Str.joinWith (List.map modu \m -> Str.concat m "::") ""),
|
||||||
|
Text name,
|
||||||
|
Text ";",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
hasDupImports : Set { modu: (List Str), name: Str } -> Bool
|
||||||
|
hasDupImports = \set ->
|
||||||
|
nameSet =
|
||||||
|
set
|
||||||
|
|> Set.toList
|
||||||
|
|> List.map \{ modu: _, name } -> name
|
||||||
|
|> Set.fromList
|
||||||
|
|
||||||
|
Set.len nameSet != Set.len nameSet
|
||||||
|
|
||||||
|
render : RenderTree -> Str
|
||||||
|
render = \tree ->
|
||||||
|
Tuple text _ = renderInner tree 0 Bool.true
|
||||||
|
text
|
||||||
|
|
||||||
|
renderGroup : List RenderTree, Nat, Bool -> [Tuple Str Bool]
|
||||||
|
renderGroup = \list, indent, newlineBefore ->
|
||||||
|
List.walk list (Tuple "" newlineBefore) \(Tuple text nlb), item ->
|
||||||
|
Tuple ntext nla = renderInner item indent nlb
|
||||||
|
(Tuple
|
||||||
|
(Str.concat text ntext)
|
||||||
|
nla
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
renderInner : RenderTree, Nat, Bool -> [Tuple Str Bool]
|
||||||
|
renderInner = \tree, indent, newlineBefore ->
|
||||||
|
when tree is
|
||||||
|
Text text ->
|
||||||
|
result = if newlineBefore then
|
||||||
|
Str.concat (Str.repeat " " (4*indent)) text
|
||||||
|
else
|
||||||
|
text
|
||||||
|
Tuple result Bool.false
|
||||||
|
Items list -> renderGroup list indent newlineBefore
|
||||||
|
Line list ->
|
||||||
|
Tuple ntext nla = renderGroup list indent Bool.true
|
||||||
|
res = if newlineBefore then
|
||||||
|
# Already added the newline, no need!
|
||||||
|
ntext
|
||||||
|
else
|
||||||
|
Str.concat "\n" ntext
|
||||||
|
res2 = if nla then
|
||||||
|
res
|
||||||
|
else
|
||||||
|
Str.concat res "\n"
|
||||||
|
(Tuple res2 Bool.true)
|
||||||
|
Indent list -> renderGroup list (indent + 1) newlineBefore
|
||||||
|
Import {modu: _, name} ->
|
||||||
|
Tuple name Bool.false
|
||||||
|
|
||||||
|
parserTrait = \t, e ->
|
||||||
|
genCall (Import {modu: ["crate", "parser"], name: "Parser"}) [Text "'a", t, e]
|
||||||
|
|
||||||
|
parseFunction : Str, RenderTree, RenderTree, RenderTree -> RenderTree
|
||||||
|
parseFunction = \name, ty, err, body ->
|
||||||
|
Items [
|
||||||
|
Line [
|
||||||
|
Text "pub fn \(name)<'a>() -> impl ",
|
||||||
|
parserTrait ty err,
|
||||||
|
Text " {",
|
||||||
|
],
|
||||||
|
Indent [body, Line [Text ".trace(\"\(name)\")" ] ],
|
||||||
|
Line [Text "}"],
|
||||||
|
Line [Text ""],
|
||||||
|
]
|
||||||
|
|
||||||
|
Type : RenderTree
|
||||||
|
ErrTy : RenderTree
|
||||||
|
|
||||||
|
Parser : [
|
||||||
|
Loc Parser,
|
||||||
|
Specialize ErrTy Parser,
|
||||||
|
Record Str (List { name: Str, parser: Parser }),
|
||||||
|
Builtin RenderTree Type,
|
||||||
|
# Named ParserName (World -> Parser),
|
||||||
|
]
|
||||||
|
|
||||||
|
errHeader : Str -> ErrTy
|
||||||
|
errHeader = \name ->
|
||||||
|
Items [
|
||||||
|
Import { modu: ["crate", "parser"], name: "EHeader" },
|
||||||
|
Text "::",
|
||||||
|
Text name,
|
||||||
|
]
|
||||||
|
|
||||||
|
fnCall : RenderTree, List RenderTree -> RenderTree
|
||||||
|
fnCall = \fnName, args ->
|
||||||
|
Items [
|
||||||
|
fnName,
|
||||||
|
Text "(",
|
||||||
|
Items (List.intersperse args (Text ",")),
|
||||||
|
Text ")",
|
||||||
|
]
|
||||||
|
|
||||||
|
fn : RenderTree -> (List RenderTree -> RenderTree)
|
||||||
|
fn = \fnName -> \args -> fnCall fnName args
|
||||||
|
|
||||||
|
genCall : RenderTree, List RenderTree -> RenderTree
|
||||||
|
genCall = \genName, args ->
|
||||||
|
Items [
|
||||||
|
genName,
|
||||||
|
Text "<",
|
||||||
|
Items (List.intersperse args (Text ", ")),
|
||||||
|
Text ">",
|
||||||
|
]
|
||||||
|
|
||||||
|
gen : RenderTree -> (List RenderTree -> RenderTree)
|
||||||
|
gen = \genName -> \args -> genCall genName args
|
||||||
|
|
||||||
|
ref : RenderTree -> RenderTree
|
||||||
|
ref = \name -> Items [Text "&'a ", name]
|
||||||
|
|
||||||
|
slice : RenderTree -> RenderTree
|
||||||
|
slice = \name -> Items [Text "[", name, Text "]"]
|
||||||
|
|
||||||
|
refSlice : RenderTree -> RenderTree
|
||||||
|
refSlice = \name -> ref (slice name)
|
||||||
|
|
||||||
|
commentOrNewline = genCall (Import {modu: ["crate", "ast"], name: "CommentOrNewline"}) [ Text "'a" ]
|
||||||
|
|
||||||
|
exposedName = genCall (Import {modu: ["crate", "header"], name: "ExposedName"}) [ Text "'a" ]
|
||||||
|
importsEntry = genCall (Import {modu: ["crate", "header"], name: "ImportsEntry"}) [ Text "'a" ]
|
||||||
|
uppercaseIdent = genCall (Import {modu: ["crate", "ident"], name: "UppercaseIdent"}) [ Text "'a" ]
|
||||||
|
|
||||||
|
moduleName = genCall (Import {modu: ["crate", "header"], name: "ModuleName"}) [ Text "'a" ]
|
||||||
|
|
||||||
|
space0E = fn (Import { modu: ["crate", "blankspace"], name: "space0_e" })
|
||||||
|
|
||||||
|
keyword = \keywordName ->
|
||||||
|
Import { modu: ["crate", "header"], name: keywordName }
|
||||||
|
|
||||||
|
spaces = \errorName ->
|
||||||
|
Builtin (space0E [errorName]) (refSlice commentOrNewline)
|
||||||
|
|
||||||
|
loc = gen (Import {modu: ["roc_region", "all"], name: "Loc"})
|
||||||
|
|
||||||
|
keywordItem = \kw, ty ->
|
||||||
|
genCall (Import {modu: ["crate", "header"], name: "KeywordItem"}) [ Text "'a", kw, ty ]
|
||||||
|
|
||||||
|
collection = \ty ->
|
||||||
|
genCall (Import {modu: ["crate", "ast"], name: "Collection"}) [ Text "'a", ty ]
|
||||||
|
|
||||||
|
spaced = \ty ->
|
||||||
|
genCall (Import {modu: ["crate", "ast"], name: "Spaced"}) [ Text "'a", ty ]
|
||||||
|
|
||||||
|
moduleNameHelp = \err ->
|
||||||
|
Builtin (fnCall (Import {modu: ["crate", "module"], name: "module_name_help" }) [ err ]) moduleName
|
||||||
|
|
||||||
|
exposesValues =
|
||||||
|
Builtin (fnCall (Import {modu: ["crate", "module"], name: "exposes_values"}) []) (keywordItem (keyword "ExposesKeyword") (collection (loc [spaced exposedName])))
|
||||||
|
|
||||||
|
imports =
|
||||||
|
Builtin (fnCall (Import {modu: ["crate", "module"], name: "imports"}) []) (keywordItem (keyword "ImportsKeyword") (collection (loc [spaced importsEntry])))
|
||||||
|
|
||||||
|
generates =
|
||||||
|
Builtin (fnCall (Import {modu: ["crate", "module"], name: "generates"}) []) (keywordItem (keyword "GeneratesKeyword") uppercaseIdent)
|
||||||
|
|
||||||
|
generatesWith =
|
||||||
|
Builtin (fnCall (Import {modu: ["crate", "module"], name: "generates_with"}) []) (keywordItem (keyword "WithKeyword") (collection (loc [spaced exposedName])))
|
||||||
|
|
||||||
|
|
||||||
|
interfaceHeader = Record "InterfaceHeader" [
|
||||||
|
{name: "before_name", parser: spaces (errHeader "IndentStart")},
|
||||||
|
{name: "name", parser: Loc (moduleNameHelp (errHeader "ModuleName"))},
|
||||||
|
{name: "exposes", parser: Specialize (errHeader "Exposes") exposesValues },
|
||||||
|
{name: "imports", parser: Specialize (errHeader "Imports") imports },
|
||||||
|
]
|
||||||
|
|
||||||
|
hostedHeader = Record "HostedHeader" [
|
||||||
|
{name: "before_name", parser: spaces (errHeader "IndentStart")},
|
||||||
|
{name: "name", parser: Loc (moduleNameHelp (errHeader "ModuleName"))},
|
||||||
|
{name: "exposes", parser: Specialize (errHeader "Exposes") exposesValues},
|
||||||
|
{name: "imports", parser: Specialize (errHeader "Imports") imports},
|
||||||
|
{name: "generates", parser: Specialize (errHeader "Generates") generates},
|
||||||
|
{name: "generates_with", parser: Specialize (errHeader "GeneratesWith") generatesWith},
|
||||||
|
]
|
||||||
|
|
||||||
|
printCombinatorParserFunction = \parser ->
|
||||||
|
parseFunction (lowerName (resolveName parser)) (resolveType parser) (Text "EHeader<'a>") (printCombinatorParser parser)
|
||||||
|
|
||||||
|
resolveName : Parser -> Str
|
||||||
|
resolveName = \parser ->
|
||||||
|
when parser is
|
||||||
|
Loc _p -> crash "Unnamed parser!"
|
||||||
|
Specialize _err _p -> crash "Unnamed parser!"
|
||||||
|
Builtin _name _ty -> crash "Unnamed parser!"
|
||||||
|
Record name _fields -> name
|
||||||
|
|
||||||
|
underscoreScalar = 95
|
||||||
|
aLowerScalar = 97
|
||||||
|
aUpperScalar = 65
|
||||||
|
zUpperScalar = 90
|
||||||
|
|
||||||
|
# map from a lower_case_name to a UpperCaseName
|
||||||
|
upperName : Str -> Str
|
||||||
|
upperName = \name ->
|
||||||
|
result = Str.walkScalars name {text: "", needUpper: Bool.true} \{text, needUpper}, c ->
|
||||||
|
if c == underscoreScalar then
|
||||||
|
{text, needUpper: Bool.true}
|
||||||
|
else
|
||||||
|
newText =
|
||||||
|
if needUpper then
|
||||||
|
Str.appendScalar text (c - aLowerScalar + aUpperScalar) |> orCrash
|
||||||
|
else
|
||||||
|
Str.appendScalar text c |> orCrash
|
||||||
|
{text: newText, needUpper: Bool.false}
|
||||||
|
result.text
|
||||||
|
|
||||||
|
expect (upperName "hello_world") == "HelloWorld"
|
||||||
|
|
||||||
|
orCrash : Result a e -> a
|
||||||
|
orCrash = \result ->
|
||||||
|
when result is
|
||||||
|
Ok a -> a
|
||||||
|
Err _e -> crash "orCrash"
|
||||||
|
|
||||||
|
lowerName : Str -> Str
|
||||||
|
lowerName = \name ->
|
||||||
|
result = Str.walkScalars name {text: "", needUnder: Bool.false} \{text, needUnder}, c ->
|
||||||
|
newText =
|
||||||
|
if c >= aUpperScalar && c <= zUpperScalar then
|
||||||
|
if needUnder then
|
||||||
|
text
|
||||||
|
|> Str.appendScalar underscoreScalar
|
||||||
|
|> orCrash
|
||||||
|
|> Str.appendScalar (c - aUpperScalar + aLowerScalar)
|
||||||
|
|> orCrash
|
||||||
|
else
|
||||||
|
text
|
||||||
|
|> Str.appendScalar (c - aUpperScalar + aLowerScalar)
|
||||||
|
|> orCrash
|
||||||
|
else
|
||||||
|
Str.appendScalar text c |> orCrash
|
||||||
|
|
||||||
|
{text: newText, needUnder: Bool.true}
|
||||||
|
|
||||||
|
result.text
|
||||||
|
|
||||||
|
expect
|
||||||
|
theResult = (lowerName "HelloWorld")
|
||||||
|
theResult == "hello_world"
|
||||||
|
|
||||||
|
resolveType : Parser -> RenderTree
|
||||||
|
resolveType = \parser ->
|
||||||
|
when parser is
|
||||||
|
Loc p -> loc [resolveType p]
|
||||||
|
Specialize _err p -> resolveType p
|
||||||
|
Record name _fields -> Items [ Import {modu: ["crate", "generated_ast"], name}, Text "<'a>"]
|
||||||
|
Builtin _name ty -> ty
|
||||||
|
|
||||||
|
printCombinatorParser : Parser -> RenderTree
|
||||||
|
printCombinatorParser = \parser ->
|
||||||
|
when parser is
|
||||||
|
Loc p ->
|
||||||
|
printed = printCombinatorParser p
|
||||||
|
value : RenderTree
|
||||||
|
value = Items [ (Text "loc!("), printed, (Text ")") ]
|
||||||
|
value
|
||||||
|
Specialize err p ->
|
||||||
|
printed = printCombinatorParser p
|
||||||
|
Items [
|
||||||
|
Import {modu: ["crate", "parser"], name: "specialize"},
|
||||||
|
Text "(",
|
||||||
|
err,
|
||||||
|
(Text ", "),
|
||||||
|
printed,
|
||||||
|
(Text ")"),
|
||||||
|
]
|
||||||
|
Record name fields ->
|
||||||
|
Items [
|
||||||
|
Text "record!(\(name) {",
|
||||||
|
(Indent
|
||||||
|
(fields
|
||||||
|
|> List.map \f ->
|
||||||
|
Line [Text "\(f.name): ", printCombinatorParser f.parser, Text ","]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Text "})"
|
||||||
|
]
|
||||||
|
Builtin name _ty -> name
|
||||||
|
|
||||||
|
printAst : Parser -> RenderTree
|
||||||
|
printAst = \parser ->
|
||||||
|
when parser is
|
||||||
|
Record name fields ->
|
||||||
|
Items [
|
||||||
|
Line [ Text "#[derive(Clone, Debug, PartialEq)]" ],
|
||||||
|
Line [ Text "pub struct \(name)<'a> {" ],
|
||||||
|
(Indent (
|
||||||
|
fields
|
||||||
|
|> List.map \f ->
|
||||||
|
Line [Text "pub \(f.name): ", resolveType f.parser, Text ","]
|
||||||
|
)),
|
||||||
|
Line [Text "}"],
|
||||||
|
Line [Text ""],
|
||||||
|
]
|
||||||
|
_ -> crash "Not implemented"
|
||||||
|
|
||||||
|
expect (render (Text "foo")) == "foo"
|
||||||
|
expect (render (Line [Text "foo"])) == "foo\n"
|
||||||
|
expect (render (Indent [Text "foo"])) == " foo"
|
||||||
|
expect (render (Line [Indent [Text "foo"]])) == " foo\n"
|
||||||
|
|
||||||
|
expect
|
||||||
|
res = (render (Items [Text "{", Indent [Line [Text "foo"]], Text "}"]))
|
||||||
|
res ==
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
foo
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
allSyntaxItems = [interfaceHeader, hostedHeader]
|
||||||
|
|
||||||
|
printedAstItems = Items (allSyntaxItems |> List.map printAst)
|
||||||
|
printedParserItems = Items (allSyntaxItems |> List.map printCombinatorParserFunction)
|
||||||
|
|
||||||
|
|
||||||
|
# main : Task {} []*
|
||||||
|
main =
|
||||||
|
task =
|
||||||
|
_ <- File.writeUtf8 (Path.fromStr "generated_ast.rs") (renderFile printedAstItems) |> Task.await
|
||||||
|
|
||||||
|
File.writeUtf8 (Path.fromStr "generated_parser.rs") (renderFile printedParserItems)
|
||||||
|
|
||||||
|
Task.attempt task \result ->
|
||||||
|
when result is
|
||||||
|
Ok _ -> Stdout.line "Success!"
|
||||||
|
Err _e -> Stdout.line "Failed to write file"
|
|
@ -45,6 +45,7 @@ pub enum HeaderType<'a> {
|
||||||
/// usually something other than `pf`
|
/// usually something other than `pf`
|
||||||
config_shorthand: &'a str,
|
config_shorthand: &'a str,
|
||||||
exposes: &'a [Loc<ModuleName<'a>>],
|
exposes: &'a [Loc<ModuleName<'a>>],
|
||||||
|
exposes_ids: &'a [ModuleId],
|
||||||
},
|
},
|
||||||
Platform {
|
Platform {
|
||||||
opt_app_module_id: Option<ModuleId>,
|
opt_app_module_id: Option<ModuleId>,
|
||||||
|
@ -54,6 +55,7 @@ pub enum HeaderType<'a> {
|
||||||
requires: &'a [Loc<TypedIdent<'a>>],
|
requires: &'a [Loc<TypedIdent<'a>>],
|
||||||
requires_types: &'a [Loc<UppercaseIdent<'a>>],
|
requires_types: &'a [Loc<UppercaseIdent<'a>>],
|
||||||
exposes: &'a [Loc<ModuleName<'a>>],
|
exposes: &'a [Loc<ModuleName<'a>>],
|
||||||
|
exposes_ids: &'a [ModuleId],
|
||||||
|
|
||||||
/// usually `pf`
|
/// usually `pf`
|
||||||
config_shorthand: &'a str,
|
config_shorthand: &'a str,
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub fn parse_header<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
||||||
use crate::parser::keyword_e;
|
use crate::parser::keyword_e;
|
||||||
|
|
||||||
record!(Module {
|
record!(Module {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
SourceError { problem: Imports(ListEnd(@87), @65), bytes: [97, 112, 112, 32, 34, 116, 101, 115, 116, 45, 109, 105, 115, 115, 105, 110, 103, 45, 99, 111, 109, 109, 97, 34, 10, 32, 32, 32, 32, 112, 97, 99, 107, 97, 103, 101, 115, 32, 123, 32, 112, 102, 58, 32, 34, 112, 108, 97, 116, 102, 111, 114, 109, 47, 109, 97, 105, 110, 46, 114, 111, 99, 34, 32, 125, 10, 32, 32, 32, 32, 105, 109, 112, 111, 114, 116, 115, 32, 91, 112, 102, 46, 84, 97, 115, 107, 32, 66, 97, 115, 101, 54, 52, 93, 10, 32, 32, 32, 32, 112, 114, 111, 118, 105, 100, 101, 115, 32, 91, 109, 97, 105, 110, 44, 32, 64, 70, 111, 111, 93, 32, 116, 111, 32, 112, 102] }
|
|
|
@ -1 +0,0 @@
|
||||||
SourceError { problem: Space(HasMisplacedCarriageReturn, @1), bytes: [35, 13, 12, 9, 65] }
|
|
|
@ -1,41 +0,0 @@
|
||||||
Defs(
|
|
||||||
Defs {
|
|
||||||
tags: [
|
|
||||||
Index(0),
|
|
||||||
],
|
|
||||||
regions: [
|
|
||||||
@0-5,
|
|
||||||
],
|
|
||||||
space_before: [
|
|
||||||
Slice(start = 0, length = 0),
|
|
||||||
],
|
|
||||||
space_after: [
|
|
||||||
Slice(start = 0, length = 0),
|
|
||||||
],
|
|
||||||
spaces: [],
|
|
||||||
type_defs: [
|
|
||||||
Alias {
|
|
||||||
header: TypeHeader {
|
|
||||||
name: @0-1 "R",
|
|
||||||
vars: [],
|
|
||||||
},
|
|
||||||
ann: @4-5 Apply(
|
|
||||||
"",
|
|
||||||
"D",
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
value_defs: [],
|
|
||||||
},
|
|
||||||
@7-8 SpaceBefore(
|
|
||||||
Var {
|
|
||||||
module_name: "",
|
|
||||||
ident: "a",
|
|
||||||
},
|
|
||||||
[
|
|
||||||
Newline,
|
|
||||||
Newline,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
|
@ -1,3 +0,0 @@
|
||||||
R : D
|
|
||||||
|
|
||||||
a
|
|
|
@ -1,3 +0,0 @@
|
||||||
Hash has hash : a -> U64 | a has Hash
|
|
||||||
|
|
||||||
1
|
|
|
@ -1,5 +0,0 @@
|
||||||
Ab1 has ab1 : a -> {} | a has Ab1
|
|
||||||
|
|
||||||
Ab2 has ab2 : a -> {} | a has Ab2
|
|
||||||
|
|
||||||
1
|
|
|
@ -1,9 +0,0 @@
|
||||||
## first line of docs
|
|
||||||
## second line
|
|
||||||
## third line
|
|
||||||
## fourth line
|
|
||||||
##
|
|
||||||
## sixth line after doc new line
|
|
||||||
x = 5
|
|
||||||
|
|
||||||
42
|
|
|
@ -1,4 +0,0 @@
|
||||||
f : (Str)a -> (Str)a
|
|
||||||
f = \x -> x
|
|
||||||
|
|
||||||
f ("Str", 42)
|
|
|
@ -1,4 +0,0 @@
|
||||||
f : I64 -> (I64, I64)
|
|
||||||
f = \x -> (x, x + 1)
|
|
||||||
|
|
||||||
f 42
|
|
|
@ -1,5 +0,0 @@
|
||||||
my_list = [
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
]
|
|
||||||
42
|
|
|
@ -1,3 +0,0 @@
|
||||||
when x is
|
|
||||||
bar.and -> 1
|
|
||||||
_ -> 4
|
|
|
@ -1,3 +0,0 @@
|
||||||
when x is
|
|
||||||
Foo.and -> 1
|
|
||||||
_ -> 4
|
|
|
@ -1,3 +0,0 @@
|
||||||
x, y <- List.map2 [] []
|
|
||||||
|
|
||||||
x + y
|
|
|
@ -1 +0,0 @@
|
||||||
"foo"
|
|
|
@ -1,4 +0,0 @@
|
||||||
x =
|
|
||||||
5
|
|
||||||
|
|
||||||
42
|
|
|
@ -1,4 +0,0 @@
|
||||||
# leading comment
|
|
||||||
x = 5
|
|
||||||
|
|
||||||
42
|
|
|
@ -1 +0,0 @@
|
||||||
@Age
|
|
|
@ -1 +0,0 @@
|
||||||
@Age m n
|
|
|
@ -1,3 +0,0 @@
|
||||||
Blah a b : Foo.Bar.Baz x y
|
|
||||||
|
|
||||||
42
|
|
|
@ -1,3 +0,0 @@
|
||||||
foo : Foo.Bar.Baz x y as Blah a b
|
|
||||||
|
|
||||||
42
|
|
|
@ -1 +0,0 @@
|
||||||
1 * if Bool.true then 1 else 1
|
|
|
@ -1,5 +0,0 @@
|
||||||
# leading comment
|
|
||||||
{ x, y } = 5
|
|
||||||
y = 6
|
|
||||||
|
|
||||||
42
|
|
|
@ -1,3 +0,0 @@
|
||||||
x : { init : {} -> Model, update : Model, Str -> Model, view : Model -> Str }
|
|
||||||
|
|
||||||
42
|
|
|
@ -1,5 +0,0 @@
|
||||||
# leading comment
|
|
||||||
x = 5
|
|
||||||
y = 6
|
|
||||||
|
|
||||||
42
|
|
|
@ -1,9 +0,0 @@
|
||||||
when x is
|
|
||||||
_ ->
|
|
||||||
1
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
2
|
|
||||||
|
|
||||||
Ok ->
|
|
||||||
3
|
|
|
@ -1,3 +0,0 @@
|
||||||
f : a -> (b -> c) | a has A
|
|
||||||
|
|
||||||
f
|
|
|
@ -1,3 +0,0 @@
|
||||||
f : a -> (b -> c) | a has A, b has Eq, c has Ord
|
|
||||||
|
|
||||||
f
|
|
|
@ -1,3 +0,0 @@
|
||||||
f : a | a has A
|
|
||||||
|
|
||||||
f
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
/// Simple tests for parsing expressions, module headers, module defs, etc.
|
||||||
|
/// Note, much more extensive tests are in the `test_syntax` crate (in test_snapshots).
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate pretty_assertions;
|
extern crate pretty_assertions;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -24,420 +27,8 @@ mod test_parse {
|
||||||
use roc_parse::state::State;
|
use roc_parse::state::State;
|
||||||
use roc_parse::test_helpers::parse_expr_with;
|
use roc_parse::test_helpers::parse_expr_with;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_test_utils::assert_multiline_str_eq;
|
|
||||||
use std::{f64, i64};
|
use std::{f64, i64};
|
||||||
|
|
||||||
macro_rules! parse_snapshot_kind {
|
|
||||||
(expr => $arena:expr, $input:expr) => {
|
|
||||||
parse_expr_with($arena, $input.trim())
|
|
||||||
};
|
|
||||||
(header => $arena:expr, $input:expr) => {
|
|
||||||
roc_parse::module::parse_header($arena, State::new($input.trim().as_bytes()))
|
|
||||||
.map(|tuple| tuple.0)
|
|
||||||
};
|
|
||||||
(module => $arena:expr, $input:expr) => {
|
|
||||||
module_defs()
|
|
||||||
.parse($arena, State::new($input.as_bytes()), 0)
|
|
||||||
.map(|tuple| tuple.1)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! should_pass {
|
|
||||||
(pass) => {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
(fail) => {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! snapshot_tests {
|
|
||||||
(
|
|
||||||
$($pass_or_fail:ident / $test_name:ident . $kind:ident),*
|
|
||||||
$(,)?
|
|
||||||
) => {
|
|
||||||
#[test]
|
|
||||||
fn no_extra_snapshot_test_files() {
|
|
||||||
let tests = &[
|
|
||||||
$(concat!(
|
|
||||||
stringify!($pass_or_fail),
|
|
||||||
"/",
|
|
||||||
stringify!($test_name),
|
|
||||||
".",
|
|
||||||
stringify!($kind)
|
|
||||||
)),*,
|
|
||||||
].iter().map(|t| *t).collect::<std::collections::HashSet<&str>>();
|
|
||||||
|
|
||||||
fn list(dir: &std::path::Path) -> std::vec::Vec<String> {
|
|
||||||
std::fs::read_dir(dir).unwrap().map(|f| f.unwrap().file_name().to_str().unwrap().to_string()).collect::<std::vec::Vec<_>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut base = std::path::PathBuf::from("tests");
|
|
||||||
base.push("snapshots");
|
|
||||||
let pass_or_fail_names = list(&base);
|
|
||||||
let mut extra_test_files = std::collections::HashSet::new();
|
|
||||||
for res in pass_or_fail_names {
|
|
||||||
assert!(res == "pass" || res == "fail", "a pass or fail filename was neither \"pass\" nor \"fail\", but rather: {:?}", res);
|
|
||||||
let res_dir = base.join(&res);
|
|
||||||
for file in list(&res_dir) {
|
|
||||||
if file.ends_with(".expr.formatted.roc") {
|
|
||||||
// These are written by fmt tests for verification. Ignore them!
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let test = if let Some(test) = file.strip_suffix(".roc") {
|
|
||||||
test
|
|
||||||
} else if let Some(test) = file.strip_suffix(".result-ast") {
|
|
||||||
test
|
|
||||||
} else {
|
|
||||||
panic!("unexpected file found in tests/snapshots: {}", file);
|
|
||||||
};
|
|
||||||
let test_name = format!("{}/{}", &res, test);
|
|
||||||
if !tests.contains(test_name.as_str()) {
|
|
||||||
extra_test_files.insert(test_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if extra_test_files.len() > 0 {
|
|
||||||
eprintln!("Found extra test files:");
|
|
||||||
for file in extra_test_files {
|
|
||||||
eprintln!("{}", file);
|
|
||||||
}
|
|
||||||
panic!("Add entries for these in the `snapshot_tests!` macro in test_parse.rs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
#[test]
|
|
||||||
fn $test_name() {
|
|
||||||
snapshot_test(should_pass!($pass_or_fail), stringify!($test_name), stringify!($kind), |input| {
|
|
||||||
let arena = Bump::new();
|
|
||||||
let result = parse_snapshot_kind!($kind => &arena, input);
|
|
||||||
result
|
|
||||||
.map(|actual_ast| format!("{:#?}\n", actual_ast))
|
|
||||||
.map_err(|error| format!("{:?}", error))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// see tests/snapshots to see test input(.roc) and expected output(.result-ast)
|
|
||||||
snapshot_tests! {
|
|
||||||
fail/ability_demand_value_has_args.expr,
|
|
||||||
fail/ability_demands_not_indented_with_first.expr,
|
|
||||||
fail/ability_first_demand_not_indented_enough.expr,
|
|
||||||
fail/ability_non_signature_expression.expr,
|
|
||||||
fail/alias_or_opaque_fail.expr,
|
|
||||||
fail/comment_with_tab.expr,
|
|
||||||
fail/def_missing_final_expression.expr,
|
|
||||||
fail/double_plus.expr,
|
|
||||||
fail/elm_function_syntax.expr,
|
|
||||||
fail/empty_or_pattern.expr,
|
|
||||||
fail/error_inline_alias_argument_uppercase.expr,
|
|
||||||
fail/error_inline_alias_not_an_alias.expr,
|
|
||||||
fail/error_inline_alias_qualified.expr,
|
|
||||||
fail/expr_to_pattern_fail.expr,
|
|
||||||
fail/expression_indentation_end.expr,
|
|
||||||
fail/if_guard_without_condition.expr,
|
|
||||||
fail/if_missing_else.expr,
|
|
||||||
fail/if_outdented_then.expr,
|
|
||||||
fail/imports_missing_comma.header,
|
|
||||||
fail/inline_hastype.expr,
|
|
||||||
fail/invalid_operator.expr,
|
|
||||||
fail/lambda_double_comma.expr,
|
|
||||||
fail/lambda_extra_comma.expr,
|
|
||||||
fail/lambda_leading_comma.expr,
|
|
||||||
fail/lambda_missing_indent.expr,
|
|
||||||
fail/list_double_comma.expr,
|
|
||||||
fail/list_pattern_not_terminated.expr,
|
|
||||||
fail/list_pattern_weird_indent.expr,
|
|
||||||
fail/list_pattern_weird_rest_pattern.expr,
|
|
||||||
fail/list_without_end.expr,
|
|
||||||
fail/multi_no_end.expr,
|
|
||||||
fail/pattern_binds_keyword.expr,
|
|
||||||
fail/pattern_in_parens_end_comma.expr,
|
|
||||||
fail/pattern_in_parens_end.expr,
|
|
||||||
fail/pattern_in_parens_indent_open.expr,
|
|
||||||
fail/pattern_in_parens_open.expr,
|
|
||||||
fail/record_type_end.expr,
|
|
||||||
fail/record_type_keyword_field_name.expr,
|
|
||||||
fail/record_type_missing_comma.expr,
|
|
||||||
fail/record_type_open_indent.expr,
|
|
||||||
fail/record_type_open.expr,
|
|
||||||
fail/record_type_tab.expr,
|
|
||||||
fail/single_no_end.expr,
|
|
||||||
fail/tab_crash.header,
|
|
||||||
fail/tag_union_end.expr,
|
|
||||||
fail/tag_union_lowercase_tag_name.expr,
|
|
||||||
fail/tag_union_open.expr,
|
|
||||||
fail/tag_union_second_lowercase_tag_name.expr,
|
|
||||||
fail/trailing_operator.expr,
|
|
||||||
fail/type_annotation_double_colon.expr,
|
|
||||||
fail/type_apply_stray_dot.expr,
|
|
||||||
fail/type_argument_arrow_then_nothing.expr,
|
|
||||||
fail/type_argument_no_arrow.expr,
|
|
||||||
fail/type_double_comma.expr,
|
|
||||||
fail/type_in_parens_end.expr,
|
|
||||||
fail/type_in_parens_start.expr,
|
|
||||||
fail/type_inline_alias.expr,
|
|
||||||
fail/underscore_name_type_annotation.expr,
|
|
||||||
fail/unfinished_closure_pattern_in_parens.expr,
|
|
||||||
fail/unicode_not_hex.expr,
|
|
||||||
fail/weird_escape.expr,
|
|
||||||
fail/when_missing_arrow.expr,
|
|
||||||
fail/when_outdented_branch.expr,
|
|
||||||
fail/when_over_indented_int.expr,
|
|
||||||
fail/when_over_indented_underscore.expr,
|
|
||||||
fail/wild_case_arrow.expr,
|
|
||||||
pass/ability_demand_signature_is_multiline.expr,
|
|
||||||
pass/ability_multi_line.expr,
|
|
||||||
pass/ability_single_line.expr,
|
|
||||||
pass/ability_two_in_a_row.expr,
|
|
||||||
pass/add_var_with_spaces.expr,
|
|
||||||
pass/add_with_spaces.expr,
|
|
||||||
pass/annotated_record_destructure.expr,
|
|
||||||
pass/annotated_tag_destructure.expr,
|
|
||||||
pass/annotated_tuple_destructure.expr,
|
|
||||||
pass/apply_parenthetical_tag_args.expr,
|
|
||||||
pass/apply_tag.expr,
|
|
||||||
pass/apply_three_args.expr,
|
|
||||||
pass/apply_two_args.expr,
|
|
||||||
pass/apply_unary_negation.expr,
|
|
||||||
pass/apply_unary_not.expr,
|
|
||||||
pass/bad_opaque_ref.expr,
|
|
||||||
pass/basic_apply.expr,
|
|
||||||
pass/basic_docs.expr,
|
|
||||||
pass/basic_field.expr,
|
|
||||||
pass/basic_tag.expr,
|
|
||||||
pass/basic_tuple.expr,
|
|
||||||
pass/basic_var.expr,
|
|
||||||
pass/closure_with_underscores.expr,
|
|
||||||
pass/comment_after_def.module,
|
|
||||||
pass/comment_after_op.expr,
|
|
||||||
pass/comment_before_op.expr,
|
|
||||||
pass/comment_inside_empty_list.expr,
|
|
||||||
pass/comment_with_non_ascii.expr,
|
|
||||||
pass/crash.expr,
|
|
||||||
pass/dbg.expr,
|
|
||||||
pass/def_without_newline.expr,
|
|
||||||
pass/destructure_tag_assignment.expr,
|
|
||||||
pass/empty_app_header.header,
|
|
||||||
pass/empty_hosted_header.header,
|
|
||||||
pass/empty_interface_header.header,
|
|
||||||
pass/empty_list.expr,
|
|
||||||
pass/empty_package_header.header,
|
|
||||||
pass/empty_platform_header.header,
|
|
||||||
pass/empty_record.expr,
|
|
||||||
pass/empty_string.expr,
|
|
||||||
pass/equals_with_spaces.expr,
|
|
||||||
pass/equals.expr,
|
|
||||||
pass/expect_fx.module,
|
|
||||||
pass/expect.expr,
|
|
||||||
pass/float_with_underscores.expr,
|
|
||||||
pass/full_app_header_trailing_commas.header,
|
|
||||||
pass/full_app_header.header,
|
|
||||||
pass/function_effect_types.header,
|
|
||||||
pass/function_with_tuple_ext_type.expr,
|
|
||||||
pass/function_with_tuple_type.expr,
|
|
||||||
pass/highest_float.expr,
|
|
||||||
pass/highest_int.expr,
|
|
||||||
pass/if_def.expr,
|
|
||||||
pass/int_with_underscore.expr,
|
|
||||||
pass/interface_with_newline.header,
|
|
||||||
pass/lambda_in_chain.expr,
|
|
||||||
pass/lambda_indent.expr,
|
|
||||||
pass/list_closing_indent_not_enough.expr,
|
|
||||||
pass/list_closing_same_indent_no_trailing_comma.expr,
|
|
||||||
pass/list_closing_same_indent_with_trailing_comma.expr,
|
|
||||||
pass/list_patterns.expr,
|
|
||||||
pass/lowest_float.expr,
|
|
||||||
pass/lowest_int.expr,
|
|
||||||
pass/malformed_ident_due_to_underscore.expr,
|
|
||||||
pass/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399
|
|
||||||
pass/malformed_pattern_module_name.expr, // See https://github.com/roc-lang/roc/issues/399
|
|
||||||
pass/minimal_app_header.header,
|
|
||||||
pass/minus_twelve_minus_five.expr,
|
|
||||||
pass/mixed_docs.expr,
|
|
||||||
pass/module_def_newline.module,
|
|
||||||
pass/multi_backpassing.expr,
|
|
||||||
pass/multi_char_string.expr,
|
|
||||||
pass/multiline_string.expr,
|
|
||||||
pass/multiline_tuple_with_comments.expr,
|
|
||||||
pass/multiline_type_signature_with_comment.expr,
|
|
||||||
pass/multiline_type_signature.expr,
|
|
||||||
pass/multiple_fields.expr,
|
|
||||||
pass/multiple_operators.expr,
|
|
||||||
pass/neg_inf_float.expr,
|
|
||||||
pass/negative_float.expr,
|
|
||||||
pass/negative_int.expr,
|
|
||||||
pass/nested_def_annotation.module,
|
|
||||||
pass/nested_if.expr,
|
|
||||||
pass/nested_module.header,
|
|
||||||
pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51
|
|
||||||
pass/newline_after_mul.expr,
|
|
||||||
pass/newline_after_sub.expr,
|
|
||||||
pass/newline_and_spaces_before_less_than.expr,
|
|
||||||
pass/newline_before_add.expr,
|
|
||||||
pass/newline_before_sub.expr,
|
|
||||||
pass/newline_inside_empty_list.expr,
|
|
||||||
pass/newline_singleton_list.expr,
|
|
||||||
pass/nonempty_hosted_header.header,
|
|
||||||
pass/nonempty_package_header.header,
|
|
||||||
pass/nonempty_platform_header.header,
|
|
||||||
pass/not_docs.expr,
|
|
||||||
pass/number_literal_suffixes.expr,
|
|
||||||
pass/one_backpassing.expr,
|
|
||||||
pass/one_char_string.expr,
|
|
||||||
pass/one_def.expr,
|
|
||||||
pass/one_minus_two.expr,
|
|
||||||
pass/one_plus_two.expr,
|
|
||||||
pass/one_spaced_def.expr,
|
|
||||||
pass/opaque_destructure_first_item_in_body.expr,
|
|
||||||
pass/opaque_has_abilities.expr,
|
|
||||||
pass/opaque_reference_expr_with_arguments.expr,
|
|
||||||
pass/opaque_reference_expr.expr,
|
|
||||||
pass/opaque_reference_pattern_with_arguments.expr,
|
|
||||||
pass/opaque_reference_pattern.expr,
|
|
||||||
pass/opaque_simple.module,
|
|
||||||
pass/opaque_with_type_arguments.module,
|
|
||||||
pass/ops_with_newlines.expr,
|
|
||||||
pass/outdented_app_with_record.expr,
|
|
||||||
pass/outdented_list.expr,
|
|
||||||
pass/outdented_record.expr,
|
|
||||||
pass/packed_singleton_list.expr,
|
|
||||||
pass/parenthetical_apply.expr,
|
|
||||||
pass/parenthetical_basic_field.expr,
|
|
||||||
pass/parenthetical_field_qualified_var.expr,
|
|
||||||
pass/parenthetical_var.expr,
|
|
||||||
pass/parse_alias.expr,
|
|
||||||
pass/parse_as_ann.expr,
|
|
||||||
pass/pattern_with_space_in_parens.expr, // https://github.com/roc-lang/roc/issues/929
|
|
||||||
pass/plus_if.expr,
|
|
||||||
pass/plus_when.expr,
|
|
||||||
pass/pos_inf_float.expr,
|
|
||||||
pass/positive_float.expr,
|
|
||||||
pass/positive_int.expr,
|
|
||||||
pass/provides_type.header,
|
|
||||||
pass/qualified_field.expr,
|
|
||||||
pass/qualified_tag.expr,
|
|
||||||
pass/qualified_var.expr,
|
|
||||||
pass/record_access_after_tuple.expr,
|
|
||||||
pass/record_destructure_def.expr,
|
|
||||||
pass/record_func_type_decl.expr,
|
|
||||||
pass/record_type_with_function.expr,
|
|
||||||
pass/record_update.expr,
|
|
||||||
pass/record_with_if.expr,
|
|
||||||
pass/requires_type.header,
|
|
||||||
pass/single_arg_closure.expr,
|
|
||||||
pass/single_underscore_closure.expr,
|
|
||||||
pass/space_only_after_minus.expr,
|
|
||||||
pass/spaced_singleton_list.expr,
|
|
||||||
pass/spaces_inside_empty_list.expr,
|
|
||||||
pass/standalone_module_defs.module,
|
|
||||||
pass/string_without_escape.expr,
|
|
||||||
pass/sub_var_with_spaces.expr,
|
|
||||||
pass/sub_with_spaces.expr,
|
|
||||||
pass/tag_pattern.expr,
|
|
||||||
pass/ten_times_eleven.expr,
|
|
||||||
pass/three_arg_closure.expr,
|
|
||||||
pass/tuple_access_after_record.expr,
|
|
||||||
pass/tuple_accessor_function.expr,
|
|
||||||
pass/tuple_type_ext.expr,
|
|
||||||
pass/tuple_type.expr,
|
|
||||||
pass/two_arg_closure.expr,
|
|
||||||
pass/two_backpassing.expr,
|
|
||||||
pass/two_branch_when.expr,
|
|
||||||
pass/two_spaced_def.expr,
|
|
||||||
pass/abcdplain.expr,
|
|
||||||
pass/abcdfmt.expr,
|
|
||||||
pass/type_decl_with_underscore.expr,
|
|
||||||
pass/unary_negation_access.expr, // Regression test for https://github.com/roc-lang/roc/issues/509
|
|
||||||
pass/unary_negation_arg.expr,
|
|
||||||
pass/unary_negation_with_parens.expr,
|
|
||||||
pass/unary_negation.expr,
|
|
||||||
pass/unary_not_with_parens.expr,
|
|
||||||
pass/unary_not.expr,
|
|
||||||
pass/underscore_backpassing.expr,
|
|
||||||
pass/underscore_in_assignment_pattern.expr,
|
|
||||||
pass/var_else.expr,
|
|
||||||
pass/var_if.expr,
|
|
||||||
pass/var_is.expr,
|
|
||||||
pass/var_minus_two.expr,
|
|
||||||
pass/var_then.expr,
|
|
||||||
pass/var_when.expr,
|
|
||||||
pass/when_if_guard.expr,
|
|
||||||
pass/when_in_assignment.expr,
|
|
||||||
pass/when_in_function_python_style_indent.expr,
|
|
||||||
pass/when_in_function.expr,
|
|
||||||
pass/when_in_parens_indented.expr,
|
|
||||||
pass/when_in_parens.expr,
|
|
||||||
pass/when_with_alternative_patterns.expr,
|
|
||||||
pass/when_with_function_application.expr,
|
|
||||||
pass/when_with_negative_numbers.expr,
|
|
||||||
pass/when_with_numbers.expr,
|
|
||||||
pass/when_with_records.expr,
|
|
||||||
pass/when_with_tuple_in_record.expr,
|
|
||||||
pass/when_with_tuples.expr,
|
|
||||||
pass/where_clause_function.expr,
|
|
||||||
pass/where_clause_multiple_bound_abilities.expr,
|
|
||||||
pass/where_clause_multiple_has_across_newlines.expr,
|
|
||||||
pass/where_clause_multiple_has.expr,
|
|
||||||
pass/where_clause_non_function.expr,
|
|
||||||
pass/where_clause_on_newline.expr,
|
|
||||||
pass/zero_float.expr,
|
|
||||||
pass/zero_int.expr,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn snapshot_test(
|
|
||||||
should_pass: bool,
|
|
||||||
name: &str,
|
|
||||||
ty: &str,
|
|
||||||
func: impl Fn(&str) -> Result<String, String>,
|
|
||||||
) {
|
|
||||||
let mut parent = std::path::PathBuf::from("tests");
|
|
||||||
parent.push("snapshots");
|
|
||||||
parent.push(if should_pass { "pass" } else { "fail" });
|
|
||||||
let input_path = parent.join(&format!("{}.{}.roc", name, ty));
|
|
||||||
let result_path = parent.join(&format!("{}.{}.result-ast", name, ty));
|
|
||||||
|
|
||||||
let input = std::fs::read_to_string(&input_path).unwrap_or_else(|err| {
|
|
||||||
panic!(
|
|
||||||
"Could not find a snapshot test result at {:?} - {:?}",
|
|
||||||
input_path, err
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let result = func(&input);
|
|
||||||
|
|
||||||
let actual_result = if should_pass {
|
|
||||||
result.expect("The source code for this test did not successfully parse!")
|
|
||||||
} else {
|
|
||||||
result.expect_err(
|
|
||||||
"The source code for this test successfully parsed, but it was not expected to!",
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if std::env::var("ROC_SNAPSHOT_TEST_OVERWRITE").is_ok() {
|
|
||||||
std::fs::write(&result_path, actual_result).unwrap();
|
|
||||||
} else {
|
|
||||||
let expected_result = std::fs::read_to_string(&result_path).unwrap_or_else(|e| {
|
|
||||||
panic!(
|
|
||||||
"Error opening test output file {}:\n\
|
|
||||||
{:?}
|
|
||||||
Supposing the file is missing, consider running the tests with:\n\
|
|
||||||
`env ROC_SNAPSHOT_TEST_OVERWRITE=1 cargo test ...`\n\
|
|
||||||
and committing the file that creates.",
|
|
||||||
result_path.display(),
|
|
||||||
e
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_multiline_str_eq!(expected_result, actual_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_parses_to<'a>(input: &'a str, expected_expr: Expr<'a>) {
|
fn assert_parses_to<'a>(input: &'a str, expected_expr: Expr<'a>) {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let actual = parse_expr_with(&arena, input.trim());
|
let actual = parse_expr_with(&arena, input.trim());
|
||||||
|
@ -667,207 +258,6 @@ mod test_parse {
|
||||||
assert_parses_to("'b'", Expr::SingleQuote("b"));
|
assert_parses_to("'b'", Expr::SingleQuote("b"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// RECORD LITERALS
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn type_signature_def() {
|
|
||||||
// let arena = Bump::new();
|
|
||||||
// let newline = bumpalo::vec![in &arena; Newline];
|
|
||||||
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
|
||||||
// let applied_ann = TypeAnnotation::Apply("", "Int", &[]);
|
|
||||||
// let signature = Def::Annotation(
|
|
||||||
// Located::new(0, 0, 0, 3, Identifier("foo")),
|
|
||||||
// Located::new(0, 0, 6, 9, applied_ann),
|
|
||||||
// );
|
|
||||||
// let def = Def::Body(
|
|
||||||
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
|
||||||
// arena.alloc(Located::new(1, 1, 6, 7, Num("4"))),
|
|
||||||
// );
|
|
||||||
// let spaced_def = Def::SpaceBefore(arena.alloc(def), &newline);
|
|
||||||
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 7, spaced_def));
|
|
||||||
|
|
||||||
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
|
||||||
// let defs = &[loc_ann, loc_def];
|
|
||||||
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), &newlines);
|
|
||||||
// let loc_ret = Located::new(3, 3, 0, 2, ret);
|
|
||||||
// let expected = Defs(defs, arena.alloc(loc_ret));
|
|
||||||
|
|
||||||
// assert_parses_to(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// foo : Int
|
|
||||||
// foo = 4
|
|
||||||
|
|
||||||
// 42
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// expected,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn type_signature_function_def() {
|
|
||||||
// use TypeAnnotation;
|
|
||||||
// let arena = Bump::new();
|
|
||||||
// let newline = bumpalo::vec![in &arena; Newline];
|
|
||||||
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
|
||||||
|
|
||||||
// let int_type = TypeAnnotation::Apply("", "Int", &[]);
|
|
||||||
// let float_type = TypeAnnotation::Apply("", "Float", &[]);
|
|
||||||
// let bool_type = TypeAnnotation::Apply("", "Bool", &[]);
|
|
||||||
|
|
||||||
// let arguments = bumpalo::vec![in &arena;
|
|
||||||
// Located::new(0, 0, 6, 9, int_type),
|
|
||||||
// Located::new(0, 0, 11, 16, float_type)
|
|
||||||
// ];
|
|
||||||
// let return_type = Located::new(0, 0, 20, 24, bool_type);
|
|
||||||
// let fn_ann = TypeAnnotation::Function(&arguments, &return_type);
|
|
||||||
// let signature = Def::Annotation(
|
|
||||||
// Located::new(0, 0, 0, 3, Identifier("foo")),
|
|
||||||
// Located::new(0, 0, 20, 24, fn_ann),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let args = bumpalo::vec![in &arena;
|
|
||||||
// Located::new(1,1,7,8, Identifier("x")),
|
|
||||||
// Located::new(1,1,10,11, Underscore)
|
|
||||||
// ];
|
|
||||||
// let body = Located::new(1, 1, 15, 17, Num("42"));
|
|
||||||
|
|
||||||
// let closure = Expr::Closure(&args, &body);
|
|
||||||
|
|
||||||
// let def = Def::Body(
|
|
||||||
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
|
||||||
// arena.alloc(Located::new(1, 1, 6, 17, closure)),
|
|
||||||
// );
|
|
||||||
// let spaced = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
|
||||||
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 17, spaced));
|
|
||||||
|
|
||||||
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
|
||||||
// let defs = &[loc_ann, loc_def];
|
|
||||||
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
|
||||||
// let loc_ret = Located::new(3, 3, 0, 2, ret);
|
|
||||||
// let expected = Defs(defs, arena.alloc(loc_ret));
|
|
||||||
|
|
||||||
// assert_parses_to(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// foo : Int, Float -> Bool
|
|
||||||
// foo = \x, _ -> 42
|
|
||||||
|
|
||||||
// 42
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// expected,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn ann_open_union() {
|
|
||||||
// let arena = Bump::new();
|
|
||||||
// let newline = bumpalo::vec![in &arena; Newline];
|
|
||||||
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
|
||||||
// let tag1 = Tag::Apply {
|
|
||||||
// name: Located::new(0, 0, 8, 12, "True"),
|
|
||||||
// args: &[],
|
|
||||||
// };
|
|
||||||
// let tag2arg = Located::new(0, 0, 22, 27, TypeAnnotation::Apply("", "Thing", &[]));
|
|
||||||
// let tag2args = bumpalo::vec![in &arena; tag2arg];
|
|
||||||
// let tag2 = Tag::Apply {
|
|
||||||
// name: Located::new(0, 0, 14, 21, "Perhaps"),
|
|
||||||
// args: tag2args.into_bump_slice(),
|
|
||||||
// };
|
|
||||||
// let tags = bumpalo::vec![in &arena;
|
|
||||||
// Located::new(0, 0, 8, 12, tag1),
|
|
||||||
// Located::new(0, 0, 14, 27, tag2)
|
|
||||||
// ];
|
|
||||||
// let loc_wildcard = Located::new(0, 0, 29, 30, TypeAnnotation::Wildcard);
|
|
||||||
// let applied_ann = TypeAnnotation::TagUnion {
|
|
||||||
// tags: tags.into_bump_slice(),
|
|
||||||
// ext: Some(arena.alloc(loc_wildcard)),
|
|
||||||
// };
|
|
||||||
// let signature = Def::Annotation(
|
|
||||||
// Located::new(0, 0, 0, 3, Identifier("foo")),
|
|
||||||
// Located::new(0, 0, 6, 30, applied_ann),
|
|
||||||
// );
|
|
||||||
// let def = Def::Body(
|
|
||||||
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
|
||||||
// arena.alloc(Located::new(1, 1, 6, 10, Expr::Tag("True"))),
|
|
||||||
// );
|
|
||||||
// let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
|
||||||
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
|
||||||
|
|
||||||
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
|
||||||
// let defs = &[loc_ann, loc_def];
|
|
||||||
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
|
||||||
// let loc_ret = Located::new(3, 3, 0, 2, ret);
|
|
||||||
// let expected = Defs(defs, arena.alloc(loc_ret));
|
|
||||||
|
|
||||||
// assert_parses_to(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// foo : [True, Perhaps Thing]*
|
|
||||||
// foo = True
|
|
||||||
|
|
||||||
// 42
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// expected,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[test]
|
|
||||||
// fn ann_closed_union() {
|
|
||||||
// let arena = Bump::new();
|
|
||||||
// let newline = bumpalo::vec![in &arena; Newline];
|
|
||||||
// let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
|
||||||
// let tag1 = Tag::Apply {
|
|
||||||
// name: Located::new(0, 0, 8, 12, "True"),
|
|
||||||
// args: &[],
|
|
||||||
// };
|
|
||||||
// let tag2arg = Located::new(0, 0, 22, 27, TypeAnnotation::Apply("", "Thing", &[]));
|
|
||||||
// let tag2args = bumpalo::vec![in &arena; tag2arg];
|
|
||||||
// let tag2 = Tag::Apply {
|
|
||||||
// name: Located::new(0, 0, 14, 21, "Perhaps"),
|
|
||||||
// args: tag2args.into_bump_slice(),
|
|
||||||
// };
|
|
||||||
// let tags = bumpalo::vec![in &arena;
|
|
||||||
// Located::new(0, 0, 8, 12, tag1),
|
|
||||||
// Located::new(0, 0, 14, 27, tag2)
|
|
||||||
// ];
|
|
||||||
// let applied_ann = TypeAnnotation::TagUnion {
|
|
||||||
// tags: tags.into_bump_slice(),
|
|
||||||
// ext: None,
|
|
||||||
// };
|
|
||||||
// let signature = Def::Annotation(
|
|
||||||
// Located::new(0, 0, 0, 3, Identifier("foo")),
|
|
||||||
// Located::new(0, 0, 6, 29, applied_ann),
|
|
||||||
// );
|
|
||||||
// let def = Def::Body(
|
|
||||||
// arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
|
|
||||||
// arena.alloc(Located::new(1, 1, 6, 10, Expr::Tag("True"))),
|
|
||||||
// );
|
|
||||||
// let spaced_def = Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice());
|
|
||||||
// let loc_def = &*arena.alloc(Located::new(1, 1, 0, 10, spaced_def));
|
|
||||||
|
|
||||||
// let loc_ann = &*arena.alloc(Located::new(0, 0, 0, 3, signature));
|
|
||||||
// let defs = &[loc_ann, loc_def];
|
|
||||||
// let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
|
||||||
// let loc_ret = Located::new(3, 3, 0, 2, ret);
|
|
||||||
// let expected = Defs(defs, arena.alloc(loc_ret));
|
|
||||||
|
|
||||||
// assert_parses_to(
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// foo : [True, Perhaps Thing]
|
|
||||||
// foo = True
|
|
||||||
|
|
||||||
// 42
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// expected,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn repro_keyword_bug() {
|
fn repro_keyword_bug() {
|
||||||
// Reproducing this bug requires a bizarre set of things to all be true:
|
// Reproducing this bug requires a bizarre set of things to all be true:
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.23;
|
||||||
|
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.24;
|
||||||
|
|
||||||
|
procedure List.26 (List.152, List.153, List.154):
|
||||||
|
let List.493 : [C U64, C U64] = CallByName List.90 List.152 List.153 List.154;
|
||||||
|
let List.496 : U8 = 1i64;
|
||||||
|
let List.497 : U8 = GetTagId List.493;
|
||||||
|
let List.498 : Int1 = lowlevel Eq List.496 List.497;
|
||||||
|
if List.498 then
|
||||||
|
let List.155 : U64 = UnionAtIndex (Id 1) (Index 0) List.493;
|
||||||
|
ret List.155;
|
||||||
|
else
|
||||||
|
let List.156 : U64 = UnionAtIndex (Id 0) (Index 0) List.493;
|
||||||
|
ret List.156;
|
||||||
|
|
||||||
|
procedure List.26 (List.152, List.153, List.154):
|
||||||
|
let List.515 : [C I64, C I64] = CallByName List.90 List.152 List.153 List.154;
|
||||||
|
let List.518 : U8 = 1i64;
|
||||||
|
let List.519 : U8 = GetTagId List.515;
|
||||||
|
let List.520 : Int1 = lowlevel Eq List.518 List.519;
|
||||||
|
if List.520 then
|
||||||
|
let List.155 : I64 = UnionAtIndex (Id 1) (Index 0) List.515;
|
||||||
|
ret List.155;
|
||||||
|
else
|
||||||
|
let List.156 : I64 = UnionAtIndex (Id 0) (Index 0) List.515;
|
||||||
|
ret List.156;
|
||||||
|
|
||||||
|
procedure List.29 (List.294, List.295):
|
||||||
|
let List.492 : U64 = CallByName List.6 List.294;
|
||||||
|
let List.296 : U64 = CallByName Num.77 List.492 List.295;
|
||||||
|
let List.478 : List U8 = CallByName List.43 List.294 List.296;
|
||||||
|
ret List.478;
|
||||||
|
|
||||||
|
procedure List.43 (List.292, List.293):
|
||||||
|
let List.490 : U64 = CallByName List.6 List.292;
|
||||||
|
let List.489 : U64 = CallByName Num.77 List.490 List.293;
|
||||||
|
let List.480 : {U64, U64} = Struct {List.293, List.489};
|
||||||
|
let List.479 : List U8 = CallByName List.49 List.292 List.480;
|
||||||
|
ret List.479;
|
||||||
|
|
||||||
|
procedure List.49 (List.366, List.367):
|
||||||
|
let List.487 : U64 = StructAtIndex 0 List.367;
|
||||||
|
let List.488 : U64 = 0i64;
|
||||||
|
let List.485 : Int1 = CallByName Bool.11 List.487 List.488;
|
||||||
|
if List.485 then
|
||||||
|
dec List.366;
|
||||||
|
let List.486 : List U8 = Array [];
|
||||||
|
ret List.486;
|
||||||
|
else
|
||||||
|
let List.482 : U64 = StructAtIndex 1 List.367;
|
||||||
|
let List.483 : U64 = StructAtIndex 0 List.367;
|
||||||
|
let List.481 : List U8 = CallByName List.72 List.366 List.482 List.483;
|
||||||
|
ret List.481;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2):
|
||||||
|
let List.491 : U64 = lowlevel ListLen #Attr.2;
|
||||||
|
ret List.491;
|
||||||
|
|
||||||
|
procedure List.66 (#Attr.2, #Attr.3):
|
||||||
|
let List.514 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||||
|
ret List.514;
|
||||||
|
|
||||||
|
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
|
||||||
|
let List.484 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||||
|
ret List.484;
|
||||||
|
|
||||||
|
procedure List.90 (List.426, List.427, List.428):
|
||||||
|
let List.500 : U64 = 0i64;
|
||||||
|
let List.501 : U64 = CallByName List.6 List.426;
|
||||||
|
let List.499 : [C U64, C U64] = CallByName List.91 List.426 List.427 List.428 List.500 List.501;
|
||||||
|
ret List.499;
|
||||||
|
|
||||||
|
procedure List.90 (List.426, List.427, List.428):
|
||||||
|
let List.522 : U64 = 0i64;
|
||||||
|
let List.523 : U64 = CallByName List.6 List.426;
|
||||||
|
let List.521 : [C I64, C I64] = CallByName List.91 List.426 List.427 List.428 List.522 List.523;
|
||||||
|
ret List.521;
|
||||||
|
|
||||||
|
procedure List.91 (List.549, List.550, List.551, List.552, List.553):
|
||||||
|
joinpoint List.502 List.429 List.430 List.431 List.432 List.433:
|
||||||
|
let List.504 : Int1 = CallByName Num.22 List.432 List.433;
|
||||||
|
if List.504 then
|
||||||
|
let List.513 : U8 = CallByName List.66 List.429 List.432;
|
||||||
|
let List.505 : [C U64, C U64] = CallByName Test.4 List.430 List.513;
|
||||||
|
let List.510 : U8 = 1i64;
|
||||||
|
let List.511 : U8 = GetTagId List.505;
|
||||||
|
let List.512 : Int1 = lowlevel Eq List.510 List.511;
|
||||||
|
if List.512 then
|
||||||
|
let List.434 : U64 = UnionAtIndex (Id 1) (Index 0) List.505;
|
||||||
|
let List.508 : U64 = 1i64;
|
||||||
|
let List.507 : U64 = CallByName Num.19 List.432 List.508;
|
||||||
|
jump List.502 List.429 List.434 List.431 List.507 List.433;
|
||||||
|
else
|
||||||
|
let List.435 : U64 = UnionAtIndex (Id 0) (Index 0) List.505;
|
||||||
|
let List.509 : [C U64, C U64] = TagId(0) List.435;
|
||||||
|
ret List.509;
|
||||||
|
else
|
||||||
|
let List.503 : [C U64, C U64] = TagId(1) List.430;
|
||||||
|
ret List.503;
|
||||||
|
in
|
||||||
|
jump List.502 List.549 List.550 List.551 List.552 List.553;
|
||||||
|
|
||||||
|
procedure List.91 (List.562, List.563, List.564, List.565, List.566):
|
||||||
|
joinpoint List.524 List.429 List.430 List.431 List.432 List.433:
|
||||||
|
let List.526 : Int1 = CallByName Num.22 List.432 List.433;
|
||||||
|
if List.526 then
|
||||||
|
let List.535 : U8 = CallByName List.66 List.429 List.432;
|
||||||
|
let List.527 : [C I64, C I64] = CallByName Test.4 List.430 List.535;
|
||||||
|
let List.532 : U8 = 1i64;
|
||||||
|
let List.533 : U8 = GetTagId List.527;
|
||||||
|
let List.534 : Int1 = lowlevel Eq List.532 List.533;
|
||||||
|
if List.534 then
|
||||||
|
let List.434 : I64 = UnionAtIndex (Id 1) (Index 0) List.527;
|
||||||
|
let List.530 : U64 = 1i64;
|
||||||
|
let List.529 : U64 = CallByName Num.19 List.432 List.530;
|
||||||
|
jump List.524 List.429 List.434 List.431 List.529 List.433;
|
||||||
|
else
|
||||||
|
let List.435 : I64 = UnionAtIndex (Id 0) (Index 0) List.527;
|
||||||
|
let List.531 : [C I64, C I64] = TagId(0) List.435;
|
||||||
|
ret List.531;
|
||||||
|
else
|
||||||
|
let List.525 : [C I64, C I64] = TagId(1) List.430;
|
||||||
|
ret List.525;
|
||||||
|
in
|
||||||
|
jump List.524 List.562 List.563 List.564 List.565 List.566;
|
||||||
|
|
||||||
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
|
let Num.259 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
|
ret Num.259;
|
||||||
|
|
||||||
|
procedure Num.22 (#Attr.2, #Attr.3):
|
||||||
|
let Num.261 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||||
|
ret Num.261;
|
||||||
|
|
||||||
|
procedure Num.77 (#Attr.2, #Attr.3):
|
||||||
|
let Num.257 : U64 = lowlevel NumSubSaturated #Attr.2 #Attr.3;
|
||||||
|
ret Num.257;
|
||||||
|
|
||||||
|
procedure Test.1 (Test.2):
|
||||||
|
let Test.18 : I64 = 0i64;
|
||||||
|
let Test.19 : {} = Struct {};
|
||||||
|
let Test.12 : I64 = CallByName List.26 Test.2 Test.18 Test.19;
|
||||||
|
let Test.14 : U64 = 0i64;
|
||||||
|
let Test.15 : {} = Struct {};
|
||||||
|
let Test.3 : U64 = CallByName List.26 Test.2 Test.14 Test.15;
|
||||||
|
let Test.13 : I64 = 0i64;
|
||||||
|
let Test.10 : Int1 = CallByName Bool.11 Test.12 Test.13;
|
||||||
|
if Test.10 then
|
||||||
|
ret Test.2;
|
||||||
|
else
|
||||||
|
let Test.9 : List U8 = CallByName List.29 Test.2 Test.3;
|
||||||
|
ret Test.9;
|
||||||
|
|
||||||
|
procedure Test.4 (Test.5, Test.16):
|
||||||
|
let Test.17 : [C U64, C U64] = TagId(0) Test.5;
|
||||||
|
ret Test.17;
|
||||||
|
|
||||||
|
procedure Test.4 (Test.5, Test.16):
|
||||||
|
let Test.21 : [C U64, C U64] = TagId(0) Test.5;
|
||||||
|
ret Test.21;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.8 : List U8 = Array [1i64, 2i64, 3i64];
|
||||||
|
let Test.7 : List U8 = CallByName Test.1 Test.8;
|
||||||
|
ret Test.7;
|
56
crates/compiler/test_mono/generated/issue_4557.txt
Normal file
56
crates/compiler/test_mono/generated/issue_4557.txt
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.24 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.24;
|
||||||
|
|
||||||
|
procedure Bool.4 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.23 : Int1 = lowlevel Or #Attr.2 #Attr.3;
|
||||||
|
ret Bool.23;
|
||||||
|
|
||||||
|
procedure Test.1 (Test.2, Test.3):
|
||||||
|
let Test.17 : {Int1, Int1} = Struct {Test.2, Test.3};
|
||||||
|
let Test.34 : Int1 = StructAtIndex 0 Test.17;
|
||||||
|
let Test.33 : Int1 = StructAtIndex 1 Test.17;
|
||||||
|
let Test.19 : Int1 = CallByName Test.1 Test.33 Test.34;
|
||||||
|
let Test.27 : {} = Struct {};
|
||||||
|
joinpoint Test.28 Test.21:
|
||||||
|
let Test.23 : {} = Struct {};
|
||||||
|
joinpoint Test.24 Test.22:
|
||||||
|
let Test.20 : Int1 = CallByName Bool.11 Test.21 Test.22;
|
||||||
|
dec Test.22;
|
||||||
|
dec Test.21;
|
||||||
|
let Test.18 : Int1 = CallByName Bool.4 Test.19 Test.20;
|
||||||
|
ret Test.18;
|
||||||
|
in
|
||||||
|
switch Test.33:
|
||||||
|
case 0:
|
||||||
|
let Test.25 : Str = CallByName Test.9 Test.23;
|
||||||
|
jump Test.24 Test.25;
|
||||||
|
|
||||||
|
default:
|
||||||
|
let Test.26 : Str = CallByName Test.11 Test.23;
|
||||||
|
jump Test.24 Test.26;
|
||||||
|
|
||||||
|
in
|
||||||
|
switch Test.34:
|
||||||
|
case 0:
|
||||||
|
let Test.29 : Str = CallByName Test.9 Test.27;
|
||||||
|
jump Test.28 Test.29;
|
||||||
|
|
||||||
|
default:
|
||||||
|
let Test.30 : Str = CallByName Test.11 Test.27;
|
||||||
|
jump Test.28 Test.30;
|
||||||
|
|
||||||
|
|
||||||
|
procedure Test.11 (Test.36):
|
||||||
|
let Test.37 : Str = "a";
|
||||||
|
ret Test.37;
|
||||||
|
|
||||||
|
procedure Test.9 (Test.39):
|
||||||
|
let Test.40 : Str = "a";
|
||||||
|
ret Test.40;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.38 : Int1 = false;
|
||||||
|
let Test.35 : Int1 = true;
|
||||||
|
let Test.13 : Int1 = CallByName Test.1 Test.38 Test.35;
|
||||||
|
ret Test.13;
|
|
@ -62,7 +62,7 @@ procedure Decode.26 (Decode.100, Decode.101):
|
||||||
let Decode.116 : [C [C List U8, C ], C Str] = TagId(0) Decode.117;
|
let Decode.116 : [C [C List U8, C ], C Str] = TagId(0) Decode.117;
|
||||||
ret Decode.116;
|
ret Decode.116;
|
||||||
|
|
||||||
procedure Json.139 (Json.450, Json.451):
|
procedure Json.139 (Json.452, Json.453):
|
||||||
joinpoint Json.421 Json.418 Json.138:
|
joinpoint Json.421 Json.418 Json.138:
|
||||||
let Json.141 : List U8 = StructAtIndex 0 Json.418;
|
let Json.141 : List U8 = StructAtIndex 0 Json.418;
|
||||||
inc Json.141;
|
inc Json.141;
|
||||||
|
@ -91,7 +91,7 @@ procedure Json.139 (Json.450, Json.451):
|
||||||
let Json.435 : {List U8, List U8} = Struct {Json.141, Json.140};
|
let Json.435 : {List U8, List U8} = Struct {Json.141, Json.140};
|
||||||
ret Json.435;
|
ret Json.435;
|
||||||
in
|
in
|
||||||
jump Json.421 Json.450 Json.451;
|
jump Json.421 Json.452 Json.453;
|
||||||
|
|
||||||
procedure Json.143 (Json.432):
|
procedure Json.143 (Json.432):
|
||||||
let Json.433 : List U8 = StructAtIndex 1 Json.432;
|
let Json.433 : List U8 = StructAtIndex 1 Json.432;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
procedure List.5 (#Attr.2, #Attr.3):
|
||||||
|
let List.479 : U8 = GetTagId #Attr.3;
|
||||||
|
joinpoint List.480 List.478:
|
||||||
|
inc List.478;
|
||||||
|
ret List.478;
|
||||||
|
in
|
||||||
|
switch List.479:
|
||||||
|
case 0:
|
||||||
|
let List.481 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.4 #Attr.3;
|
||||||
|
decref #Attr.2;
|
||||||
|
jump List.480 List.481;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
let List.482 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.6 #Attr.3;
|
||||||
|
decref #Attr.2;
|
||||||
|
jump List.480 List.482;
|
||||||
|
|
||||||
|
default:
|
||||||
|
let List.483 : List U8 = lowlevel ListMap { xs: `#Attr.#arg1` } #Attr.2 Test.8 #Attr.3;
|
||||||
|
decref #Attr.2;
|
||||||
|
jump List.480 List.483;
|
||||||
|
|
||||||
|
|
||||||
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
|
let Num.258 : U8 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||||
|
ret Num.258;
|
||||||
|
|
||||||
|
procedure Test.4 (Test.5, #Attr.12):
|
||||||
|
let Test.1 : U8 = UnionAtIndex (Id 0) (Index 0) #Attr.12;
|
||||||
|
let Test.15 : U8 = CallByName Num.19 Test.5 Test.1;
|
||||||
|
ret Test.15;
|
||||||
|
|
||||||
|
procedure Test.6 (Test.7, #Attr.12):
|
||||||
|
let Test.2 : U8 = UnionAtIndex (Id 1) (Index 0) #Attr.12;
|
||||||
|
let Test.17 : U8 = CallByName Num.19 Test.7 Test.2;
|
||||||
|
ret Test.17;
|
||||||
|
|
||||||
|
procedure Test.8 (Test.9):
|
||||||
|
let Test.19 : U8 = CallByName Num.19 Test.9 Test.9;
|
||||||
|
ret Test.19;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.1 : U8 = 1i64;
|
||||||
|
let Test.2 : U8 = 2i64;
|
||||||
|
joinpoint Test.13 Test.3:
|
||||||
|
let Test.11 : List U8 = Array [1i64, 2i64, 3i64];
|
||||||
|
let Test.10 : List U8 = CallByName List.5 Test.11 Test.3;
|
||||||
|
ret Test.10;
|
||||||
|
in
|
||||||
|
let Test.12 : Str = "";
|
||||||
|
let Test.22 : Str = "A";
|
||||||
|
let Test.23 : Int1 = lowlevel Eq Test.22 Test.12;
|
||||||
|
dec Test.22;
|
||||||
|
if Test.23 then
|
||||||
|
dec Test.12;
|
||||||
|
let Test.14 : [C U8, C U8, C ] = TagId(0) Test.1;
|
||||||
|
jump Test.13 Test.14;
|
||||||
|
else
|
||||||
|
let Test.20 : Str = "B";
|
||||||
|
let Test.21 : Int1 = lowlevel Eq Test.20 Test.12;
|
||||||
|
dec Test.12;
|
||||||
|
dec Test.20;
|
||||||
|
if Test.21 then
|
||||||
|
let Test.16 : [C U8, C U8, C ] = TagId(1) Test.2;
|
||||||
|
jump Test.13 Test.16;
|
||||||
|
else
|
||||||
|
let Test.18 : [C U8, C U8, C ] = TagId(2) ;
|
||||||
|
jump Test.13 Test.18;
|
|
@ -0,0 +1,35 @@
|
||||||
|
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||||
|
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||||
|
ret Bool.23;
|
||||||
|
|
||||||
|
procedure Str.3 (#Attr.2, #Attr.3):
|
||||||
|
let Str.266 : Str = lowlevel StrConcat #Attr.2 #Attr.3;
|
||||||
|
ret Str.266;
|
||||||
|
|
||||||
|
procedure Test.2 (Test.7):
|
||||||
|
let Test.24 : Str = ".trace(\"";
|
||||||
|
let Test.26 : Str = "\")";
|
||||||
|
let Test.25 : Str = CallByName Str.3 Test.7 Test.26;
|
||||||
|
dec Test.26;
|
||||||
|
let Test.23 : Str = CallByName Str.3 Test.24 Test.25;
|
||||||
|
dec Test.25;
|
||||||
|
let Test.22 : [<r>C List *self, C Str] = TagId(1) Test.23;
|
||||||
|
let Test.21 : List [<r>C List *self, C Str] = Array [Test.22];
|
||||||
|
let Test.8 : [<r>C List *self, C Str] = TagId(0) Test.21;
|
||||||
|
let Test.20 : List [<r>C List *self, C Str] = Array [Test.8];
|
||||||
|
let Test.19 : [<r>C List *self, C Str] = TagId(0) Test.20;
|
||||||
|
ret Test.19;
|
||||||
|
|
||||||
|
procedure Test.3 ():
|
||||||
|
let Test.18 : Str = "interface_header";
|
||||||
|
let Test.17 : [<r>C List *self, C Str] = CallByName Test.2 Test.18;
|
||||||
|
ret Test.17;
|
||||||
|
|
||||||
|
procedure Test.0 ():
|
||||||
|
let Test.14 : [<r>C List *self, C Str] = CallByName Test.3;
|
||||||
|
let Test.16 : Str = "";
|
||||||
|
let Test.15 : [<r>C List *self, C Str] = TagId(1) Test.16;
|
||||||
|
let Test.13 : Int1 = CallByName Bool.11 Test.14 Test.15;
|
||||||
|
dec Test.15;
|
||||||
|
dec Test.14;
|
||||||
|
ret Test.13;
|
|
@ -593,7 +593,7 @@ fn record_optional_field_function_use_default() {
|
||||||
"#
|
"#
|
||||||
}
|
}
|
||||||
|
|
||||||
#[mono_test(no_check)]
|
#[mono_test(no_check = "https://github.com/roc-lang/roc/issues/4694")]
|
||||||
fn quicksort_help() {
|
fn quicksort_help() {
|
||||||
// do we still need with_larger_debug_stack?
|
// do we still need with_larger_debug_stack?
|
||||||
r#"
|
r#"
|
||||||
|
@ -1311,7 +1311,7 @@ fn issue_2583_specialize_errors_behind_unified_branches() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[mono_test(no_check)]
|
#[mono_test]
|
||||||
fn issue_2810() {
|
fn issue_2810() {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -1530,7 +1530,7 @@ fn encode_derived_record() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[mono_test(no_check)]
|
#[mono_test]
|
||||||
fn choose_correct_recursion_var_under_record() {
|
fn choose_correct_recursion_var_under_record() {
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -1925,6 +1925,28 @@ fn encode_derived_tag_one_field_string() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn polymorphic_expression_unification() {
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
RenderTree : [
|
||||||
|
Text Str,
|
||||||
|
Indent (List RenderTree),
|
||||||
|
]
|
||||||
|
parseFunction : Str -> RenderTree
|
||||||
|
parseFunction = \name ->
|
||||||
|
last = Indent [Text ".trace(\"\(name)\")" ]
|
||||||
|
Indent [last]
|
||||||
|
|
||||||
|
values = parseFunction "interface_header"
|
||||||
|
|
||||||
|
main = values == Text ""
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[mono_test]
|
#[mono_test]
|
||||||
fn encode_derived_tag_two_payloads_string() {
|
fn encode_derived_tag_two_payloads_string() {
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -2202,7 +2224,7 @@ fn issue_4749() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[mono_test(mode = "test", no_check)]
|
#[mono_test(mode = "test")]
|
||||||
fn lambda_set_with_imported_toplevels_issue_4733() {
|
fn lambda_set_with_imported_toplevels_issue_4733() {
|
||||||
indoc!(
|
indoc!(
|
||||||
r###"
|
r###"
|
||||||
|
@ -2234,3 +2256,61 @@ fn order_list_size_tests_issue_4732() {
|
||||||
"###
|
"###
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn anonymous_closure_in_polymorphic_expression_issue_4717() {
|
||||||
|
indoc!(
|
||||||
|
r###"
|
||||||
|
app "test" provides [main] to "platform"
|
||||||
|
|
||||||
|
chompWhile : (List U8) -> (List U8)
|
||||||
|
chompWhile = \input ->
|
||||||
|
index = List.walkUntil input 0 \i, _ -> Break i
|
||||||
|
|
||||||
|
if index == 0 then
|
||||||
|
input
|
||||||
|
else
|
||||||
|
List.drop input index
|
||||||
|
|
||||||
|
main = chompWhile [1u8, 2u8, 3u8]
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn list_map_take_capturing_or_noncapturing() {
|
||||||
|
indoc!(
|
||||||
|
r###"
|
||||||
|
app "test" provides [main] to "platform"
|
||||||
|
|
||||||
|
main =
|
||||||
|
x = 1u8
|
||||||
|
y = 2u8
|
||||||
|
f = when "" is
|
||||||
|
"A" ->
|
||||||
|
g = \n -> n + x
|
||||||
|
g
|
||||||
|
"B" ->
|
||||||
|
h = \n -> n + y
|
||||||
|
h
|
||||||
|
_ ->
|
||||||
|
k = \n -> n + n
|
||||||
|
k
|
||||||
|
List.map [1u8, 2u8, 3u8] f
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[mono_test]
|
||||||
|
fn issue_4557() {
|
||||||
|
indoc!(
|
||||||
|
r###"
|
||||||
|
app "test" provides [main] to "./platform"
|
||||||
|
|
||||||
|
isEqQ = \q1, q2 -> when T q1 q2 is
|
||||||
|
T (U f1) (U f2) -> Bool.or (isEqQ (U f2) (U f1)) (f1 {} == f2 {})
|
||||||
|
|
||||||
|
main = isEqQ (U \{} -> "a") (U \{} -> "a")
|
||||||
|
"###
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -10,9 +10,6 @@ pub fn mono_test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let mut mode = "exec".to_owned();
|
let mut mode = "exec".to_owned();
|
||||||
for arg in syn::parse_macro_input!(args as syn::AttributeArgs) {
|
for arg in syn::parse_macro_input!(args as syn::AttributeArgs) {
|
||||||
use syn::{Lit, Meta, MetaNameValue, NestedMeta};
|
use syn::{Lit, Meta, MetaNameValue, NestedMeta};
|
||||||
if matches!(&arg, NestedMeta::Meta(Meta::Path(p)) if p.is_ident("no_check")) {
|
|
||||||
no_check = true;
|
|
||||||
}
|
|
||||||
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue {
|
||||||
path,
|
path,
|
||||||
eq_token: _,
|
eq_token: _,
|
||||||
|
@ -22,6 +19,9 @@ pub fn mono_test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
if path.is_ident("mode") {
|
if path.is_ident("mode") {
|
||||||
mode = s.value();
|
mode = s.value();
|
||||||
}
|
}
|
||||||
|
if path.is_ident("no_check") {
|
||||||
|
no_check = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
21
crates/compiler/test_syntax/Cargo.toml
Normal file
21
crates/compiler/test_syntax/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "test_syntax"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["The Roc Contributors"]
|
||||||
|
license = "UPL-1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "Tests for the parse + fmt crates."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_collections = { path = "../collections" }
|
||||||
|
roc_region = { path = "../region" }
|
||||||
|
roc_module = { path = "../module" }
|
||||||
|
roc_parse = { path = "../parse" }
|
||||||
|
roc_fmt = { path = "../fmt" }
|
||||||
|
roc_test_utils = { path = "../../test_utils" }
|
||||||
|
bumpalo.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions.workspace = true
|
||||||
|
indoc.workspace = true
|
||||||
|
walkdir.workspace = true
|
|
@ -253,12 +253,6 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "indoc"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.131"
|
version = "0.2.131"
|
||||||
|
@ -450,24 +444,11 @@ name = "roc_fmt"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"indoc",
|
|
||||||
"pretty_assertions",
|
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_test_utils",
|
"roc_test_utils",
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "roc_fmt-fuzz"
|
|
||||||
version = "0.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bumpalo",
|
|
||||||
"libfuzzer-sys",
|
|
||||||
"roc_fmt",
|
|
||||||
"roc_parse",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -519,15 +500,6 @@ version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "same-file"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -590,6 +562,28 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_syntax"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"roc_collections",
|
||||||
|
"roc_fmt",
|
||||||
|
"roc_module",
|
||||||
|
"roc_parse",
|
||||||
|
"roc_region",
|
||||||
|
"roc_test_utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "test_syntax-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"test_syntax",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
|
@ -608,17 +602,6 @@ version = "0.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "walkdir"
|
|
||||||
version = "2.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
|
||||||
dependencies = [
|
|
||||||
"same-file",
|
|
||||||
"winapi",
|
|
||||||
"winapi-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
@ -641,15 +624,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-util"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "roc_parse-fuzz"
|
name = "test_syntax-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
authors = ["Automatically generated"]
|
authors = ["Automatically generated"]
|
||||||
publish = false
|
publish = false
|
||||||
|
@ -11,9 +11,7 @@ cargo-fuzz = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libfuzzer-sys = "0.3"
|
libfuzzer-sys = "0.3"
|
||||||
bumpalo = { version = "3.6.1", features = ["collections"] }
|
bumpalo = { version = "3.6.1", features = ["collections"] }
|
||||||
|
test_syntax = { path = "../../test_syntax" }
|
||||||
[dependencies.roc_parse]
|
|
||||||
path = ".."
|
|
||||||
|
|
||||||
# Prevent this from interfering with workspaces
|
# Prevent this from interfering with workspaces
|
||||||
[workspace]
|
[workspace]
|
||||||
|
@ -26,13 +24,7 @@ test = false
|
||||||
doc = false
|
doc = false
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "fuzz_defs"
|
name = "fuzz_module"
|
||||||
path = "fuzz_targets/fuzz_defs.rs"
|
path = "fuzz_targets/fuzz_module.rs"
|
||||||
test = false
|
|
||||||
doc = false
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "fuzz_header"
|
|
||||||
path = "fuzz_targets/fuzz_header.rs"
|
|
||||||
test = false
|
test = false
|
||||||
doc = false
|
doc = false
|
|
@ -1,11 +1,14 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use bumpalo::Bump;
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
use libfuzzer_sys::fuzz_target;
|
||||||
use roc_parse::test_helpers::parse_expr_with;
|
use bumpalo::Bump;
|
||||||
|
use test_syntax::test_helpers::Input;
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
fuzz_target!(|data: &[u8]| {
|
||||||
if let Ok(input) = std::str::from_utf8(data) {
|
if let Ok(input) = std::str::from_utf8(data) {
|
||||||
|
let input = Input::Expr(input);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let _actual = parse_expr_with(&arena, input.trim());
|
if input.parse_in(&arena).is_ok() {
|
||||||
|
input.check_invariants(|_| (), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -1,11 +1,14 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use bumpalo::Bump;
|
|
||||||
use libfuzzer_sys::fuzz_target;
|
use libfuzzer_sys::fuzz_target;
|
||||||
use roc_parse::test_helpers::parse_defs_with;
|
use bumpalo::Bump;
|
||||||
|
use test_syntax::test_helpers::Input;
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
fuzz_target!(|data: &[u8]| {
|
||||||
if let Ok(input) = std::str::from_utf8(data) {
|
if let Ok(input) = std::str::from_utf8(data) {
|
||||||
|
let input = Input::Full(input);
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let _actual = parse_defs_with(&arena, input.trim());
|
if input.parse_in(&arena).is_ok() {
|
||||||
|
input.check_invariants(|_| (), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
1
crates/compiler/test_syntax/src/lib.rs
Normal file
1
crates/compiler/test_syntax/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod test_helpers;
|
239
crates/compiler/test_syntax/src/test_helpers.rs
Normal file
239
crates/compiler/test_syntax/src/test_helpers.rs
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
use bumpalo::Bump;
|
||||||
|
use roc_fmt::{annotation::Formattable, module::fmt_module};
|
||||||
|
use roc_parse::{
|
||||||
|
ast::{Defs, Expr, Module},
|
||||||
|
module::module_defs,
|
||||||
|
parser::{Parser, SyntaxError},
|
||||||
|
state::State,
|
||||||
|
test_helpers::{parse_defs_with, parse_expr_with, parse_header_with},
|
||||||
|
};
|
||||||
|
use roc_test_utils::assert_multiline_str_eq;
|
||||||
|
|
||||||
|
use roc_fmt::spaces::RemoveSpaces;
|
||||||
|
use roc_fmt::Buf;
|
||||||
|
|
||||||
|
/// Source code to parse. Usually in the form of a test case.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Input<'a> {
|
||||||
|
/// A header (e.g. `interface "foo" ...`)
|
||||||
|
Header(&'a str),
|
||||||
|
|
||||||
|
/// A sequence of module definitions (e.g. `f = \x -> x + 1`)
|
||||||
|
ModuleDefs(&'a str),
|
||||||
|
|
||||||
|
/// A single expression
|
||||||
|
Expr(&'a str),
|
||||||
|
|
||||||
|
/// Both the header and the module defs
|
||||||
|
Full(&'a str),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owned version of `Input`
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum InputOwned {
|
||||||
|
Header(String),
|
||||||
|
ModuleDefs(String),
|
||||||
|
Expr(String),
|
||||||
|
Full(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputOwned {
|
||||||
|
fn as_ref(&self) -> Input {
|
||||||
|
match self {
|
||||||
|
InputOwned::Header(s) => Input::Header(s),
|
||||||
|
InputOwned::ModuleDefs(s) => Input::ModuleDefs(s),
|
||||||
|
InputOwned::Expr(s) => Input::Expr(s),
|
||||||
|
InputOwned::Full(s) => Input::Full(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Output AST of a successful parse
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Output<'a> {
|
||||||
|
Header(Module<'a>),
|
||||||
|
|
||||||
|
ModuleDefs(Defs<'a>),
|
||||||
|
|
||||||
|
Expr(Expr<'a>),
|
||||||
|
|
||||||
|
Full {
|
||||||
|
header: Module<'a>,
|
||||||
|
module_defs: Defs<'a>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Output<'a> {
|
||||||
|
fn format(&self) -> InputOwned {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let mut buf = Buf::new_in(&arena);
|
||||||
|
match self {
|
||||||
|
Output::Header(header) => {
|
||||||
|
fmt_module(&mut buf, header);
|
||||||
|
buf.fmt_end_of_file();
|
||||||
|
InputOwned::Header(buf.as_str().to_string())
|
||||||
|
}
|
||||||
|
Output::ModuleDefs(defs) => {
|
||||||
|
defs.format(&mut buf, 0);
|
||||||
|
buf.fmt_end_of_file();
|
||||||
|
InputOwned::ModuleDefs(buf.as_str().to_string())
|
||||||
|
}
|
||||||
|
Output::Expr(expr) => {
|
||||||
|
expr.format(&mut buf, 0);
|
||||||
|
InputOwned::Expr(buf.as_str().to_string())
|
||||||
|
}
|
||||||
|
Output::Full {
|
||||||
|
header,
|
||||||
|
module_defs,
|
||||||
|
} => {
|
||||||
|
fmt_module(&mut buf, header);
|
||||||
|
module_defs.format(&mut buf, 0);
|
||||||
|
buf.fmt_end_of_file();
|
||||||
|
InputOwned::Full(buf.as_str().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug_format_inner(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Output::Header(header) => format!("{:#?}\n", header),
|
||||||
|
Output::ModuleDefs(defs) => format!("{:#?}\n", defs),
|
||||||
|
Output::Expr(expr) => format!("{:#?}\n", expr),
|
||||||
|
Output::Full { .. } => format!("{:#?}\n", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RemoveSpaces<'a> for Output<'a> {
|
||||||
|
fn remove_spaces(&self, arena: &'a Bump) -> Self {
|
||||||
|
match self {
|
||||||
|
Output::Header(header) => Output::Header(header.remove_spaces(arena)),
|
||||||
|
Output::ModuleDefs(defs) => Output::ModuleDefs(defs.remove_spaces(arena)),
|
||||||
|
Output::Expr(expr) => Output::Expr(expr.remove_spaces(arena)),
|
||||||
|
Output::Full {
|
||||||
|
header,
|
||||||
|
module_defs,
|
||||||
|
} => Output::Full {
|
||||||
|
header: header.remove_spaces(arena),
|
||||||
|
module_defs: module_defs.remove_spaces(arena),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Input<'a> {
|
||||||
|
pub fn as_str(&self) -> &'a str {
|
||||||
|
match self {
|
||||||
|
Input::Header(s) => s,
|
||||||
|
Input::ModuleDefs(s) => s,
|
||||||
|
Input::Expr(s) => s,
|
||||||
|
Input::Full(s) => s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_in(&self, arena: &'a Bump) -> Result<Output<'a>, SyntaxError<'a>> {
|
||||||
|
match self {
|
||||||
|
Input::Header(input) => {
|
||||||
|
let header = parse_header_with(arena, input)?;
|
||||||
|
Ok(Output::Header(header))
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::ModuleDefs(input) => {
|
||||||
|
let module_defs = parse_defs_with(arena, input)?;
|
||||||
|
Ok(Output::ModuleDefs(module_defs))
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::Expr(input) => {
|
||||||
|
let expr = parse_expr_with(arena, input)?;
|
||||||
|
Ok(Output::Expr(expr))
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::Full(input) => {
|
||||||
|
let state = State::new(input.as_bytes());
|
||||||
|
|
||||||
|
let min_indent = 0;
|
||||||
|
let (_, header, state) = roc_parse::module::header()
|
||||||
|
.parse(arena, state.clone(), min_indent)
|
||||||
|
.map_err(|(_, fail)| SyntaxError::Header(fail))?;
|
||||||
|
|
||||||
|
let (_, module_defs, _state) = module_defs()
|
||||||
|
.parse(arena, state, min_indent)
|
||||||
|
.map_err(|(_, fail)| fail)?;
|
||||||
|
|
||||||
|
Ok(Output::Full {
|
||||||
|
header,
|
||||||
|
module_defs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse and re-format the given input, and pass the output to `check_formatting`
|
||||||
|
/// for verification. The expectation is that `check_formatting` assert the result matches
|
||||||
|
/// expectations (or, overwrite the expectation based on a command-line flag)
|
||||||
|
/// Optionally, based on the value of `check_idempotency`, also verify that the formatting
|
||||||
|
/// is idempotent - that if we reformat the output, we get the same result.
|
||||||
|
pub fn check_invariants(
|
||||||
|
&self,
|
||||||
|
handle_formatted_output: impl Fn(Input),
|
||||||
|
check_idempotency: bool,
|
||||||
|
) {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
let actual = self.parse_in(&arena).unwrap_or_else(|err| {
|
||||||
|
panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", self.as_str(), err);
|
||||||
|
});
|
||||||
|
|
||||||
|
let output = actual.format();
|
||||||
|
|
||||||
|
handle_formatted_output(output.as_ref());
|
||||||
|
|
||||||
|
let reparsed_ast = output.as_ref().parse_in(&arena).unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"After formatting, the source code no longer parsed!\n\n\
|
||||||
|
Parse error was: {:?}\n\n\
|
||||||
|
The original code was:\n\n{}\n\n\
|
||||||
|
The code that failed to parse:\n\n{}\n\n\
|
||||||
|
The original ast was:\n\n{:#?}\n\n",
|
||||||
|
err,
|
||||||
|
self.as_str(),
|
||||||
|
output.as_ref().as_str(),
|
||||||
|
actual
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast_normalized = actual.remove_spaces(&arena);
|
||||||
|
let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena);
|
||||||
|
|
||||||
|
// HACK!
|
||||||
|
// We compare the debug format strings of the ASTs, because I'm finding in practice that _somewhere_ deep inside the ast,
|
||||||
|
// the PartialEq implementation is returning `false` even when the Debug-formatted impl is exactly the same.
|
||||||
|
// I don't have the patience to debug this right now, so let's leave it for another day...
|
||||||
|
// TODO: fix PartialEq impl on ast types
|
||||||
|
if format!("{:?}", ast_normalized) != format!("{:?}", reparsed_ast_normalized) {
|
||||||
|
panic!(
|
||||||
|
"Formatting bug; formatting didn't reparse to the same AST (after removing spaces)\n\n\
|
||||||
|
* * * Source code before formatting:\n{}\n\n\
|
||||||
|
* * * Source code after formatting:\n{}\n\n\
|
||||||
|
* * * AST before formatting:\n{:#?}\n\n\
|
||||||
|
* * * AST after formatting:\n{:#?}\n\n",
|
||||||
|
self.as_str(),
|
||||||
|
output.as_ref().as_str(),
|
||||||
|
ast_normalized,
|
||||||
|
reparsed_ast_normalized
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now verify that the resultant formatting is _idempotent_ - i.e. that it doesn't change again if re-formatted
|
||||||
|
if check_idempotency {
|
||||||
|
let reformatted = reparsed_ast.format();
|
||||||
|
|
||||||
|
if output != reformatted {
|
||||||
|
eprintln!("Formatting bug; formatting is not stable.\nOriginal code:\n{}\n\nFormatted code:\n{}\n\n", self.as_str(), output.as_ref().as_str());
|
||||||
|
eprintln!("Reformatting the formatted code changed it again, as follows:\n\n");
|
||||||
|
|
||||||
|
assert_multiline_str_eq!(output.as_ref().as_str(), reformatted.as_ref().as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue