mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
Merge branch 'trunk' of github.com:rtfeldman/roc into wasm-runtime-error
This commit is contained in:
commit
eaf76aa879
84 changed files with 4796 additions and 2243 deletions
3
AUTHORS
3
AUTHORS
|
@ -65,3 +65,6 @@ Mats Sigge <<mats.sigge@gmail.com>>
|
||||||
Drew Lazzeri <dlazzeri1@gmail.com>
|
Drew Lazzeri <dlazzeri1@gmail.com>
|
||||||
Tom Dohrmann <erbse.13@gmx.de>
|
Tom Dohrmann <erbse.13@gmx.de>
|
||||||
Elijah Schow <elijah.schow@gmail.com>
|
Elijah Schow <elijah.schow@gmail.com>
|
||||||
|
Derek Gustafson <degustaf@gmail.com>
|
||||||
|
Philippe Vinchon <p.vinchon@gmail.com>
|
||||||
|
Pierre-Henri Trivier <phtrivier@yahoo.fr>
|
||||||
|
|
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -1709,7 +1709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46e977036f7f5139d580c7f19ad62df9cb8ebd8410bb569e73585226be80a86f"
|
checksum = "46e977036f7f5139d580c7f19ad62df9cb8ebd8410bb569e73585226be80a86f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3357,6 +3357,7 @@ dependencies = [
|
||||||
"roc_problem",
|
"roc_problem",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
|
"static_assertions 1.1.0",
|
||||||
"ven_graph",
|
"ven_graph",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3426,6 +3427,7 @@ dependencies = [
|
||||||
name = "roc_constrain"
|
name = "roc_constrain"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arrayvec 0.7.2",
|
||||||
"roc_builtins",
|
"roc_builtins",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
@ -3659,7 +3661,7 @@ dependencies = [
|
||||||
"roc_ident",
|
"roc_ident",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"snafu",
|
"snafu",
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3682,7 +3684,7 @@ dependencies = [
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
"roc_unify",
|
"roc_unify",
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
"ven_graph",
|
"ven_graph",
|
||||||
"ven_pretty",
|
"ven_pretty",
|
||||||
]
|
]
|
||||||
|
@ -3718,7 +3720,7 @@ dependencies = [
|
||||||
name = "roc_region"
|
name = "roc_region"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3760,6 +3762,7 @@ dependencies = [
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"roc_reporting",
|
"roc_reporting",
|
||||||
|
"roc_std",
|
||||||
"roc_target",
|
"roc_target",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
]
|
]
|
||||||
|
@ -3834,6 +3837,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions 0.1.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_target"
|
name = "roc_target"
|
||||||
|
@ -3858,7 +3864,7 @@ dependencies = [
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
"ven_ena",
|
"ven_ena",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4278,6 +4284,12 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f406d6ee68db6796e11ffd7b4d171864c58b7451e79ef9460ea33c287a1f89a7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -4566,7 +4578,7 @@ checksum = "1f559b464de2e2bdabcac6a210d12e9b5a5973c251e102c44c585c71d51bd78e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"rand",
|
"rand",
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM rust:1.58.0-slim-bullseye # make sure to update nixpkgs-unstable in sources.json too so that it uses the same rust version > search for cargo on unstable here: https://search.nixos.org/packages
|
FROM rust:1.58.0-slim-bullseye # make sure to update rust-toolchain.toml and nixpkgs-unstable in sources.json too so that it uses the same rust version > search for cargo on unstable here: https://search.nixos.org/packages
|
||||||
WORKDIR /earthbuild
|
WORKDIR /earthbuild
|
||||||
|
|
||||||
prep-debian:
|
prep-debian:
|
||||||
|
@ -93,7 +93,7 @@ test-rust:
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
repl_test/test_wasm.sh && sccache --show-stats
|
repl_test/test_wasm.sh && sccache --show-stats
|
||||||
# run i386 (32-bit linux) cli tests
|
# run i386 (32-bit linux) cli tests
|
||||||
RUN echo "4" | cargo run --locked --release --features="target-x86" -- --backend=x86_32 examples/benchmarks/NQueens.roc
|
RUN echo "4" | cargo run --locked --release --features="target-x86" -- --target=x86_32 examples/benchmarks/NQueens.roc
|
||||||
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
RUN --mount=type=cache,target=$SCCACHE_DIR \
|
||||||
cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
cargo test --locked --release --features with_sound --test cli_run i386 --features="i386-cli-run" && sccache --show-stats
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use bumpalo::{collections::Vec as BumpVec, Bump};
|
use bumpalo::{collections::Vec as BumpVec, Bump};
|
||||||
|
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{BumpMap, BumpMapDefault, Index, SendMap};
|
use roc_collections::all::{BumpMap, BumpMapDefault, HumanIndex, SendMap};
|
||||||
use roc_module::{
|
use roc_module::{
|
||||||
ident::{Lowercase, TagName},
|
ident::{Lowercase, TagName},
|
||||||
symbol::Symbol,
|
symbol::Symbol,
|
||||||
|
@ -163,7 +163,7 @@ pub fn constrain_expr<'a>(
|
||||||
|
|
||||||
let elem_expected = Expected::ForReason(
|
let elem_expected = Expected::ForReason(
|
||||||
Reason::ElemInList {
|
Reason::ElemInList {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
},
|
},
|
||||||
list_elem_type.shallow_clone(),
|
list_elem_type.shallow_clone(),
|
||||||
region,
|
region,
|
||||||
|
@ -339,7 +339,7 @@ pub fn constrain_expr<'a>(
|
||||||
|
|
||||||
let reason = Reason::FnArg {
|
let reason = Reason::FnArg {
|
||||||
name: opt_symbol,
|
name: opt_symbol,
|
||||||
arg_index: Index::zero_based(index),
|
arg_index: HumanIndex::zero_based(index),
|
||||||
};
|
};
|
||||||
|
|
||||||
let expected_arg = Expected::ForReason(reason, arg_type.shallow_clone(), region);
|
let expected_arg = Expected::ForReason(reason, arg_type.shallow_clone(), region);
|
||||||
|
@ -538,7 +538,7 @@ pub fn constrain_expr<'a>(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
arity,
|
arity,
|
||||||
AnnotationSource::TypedIfBranch {
|
AnnotationSource::TypedIfBranch {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
num_branches,
|
num_branches,
|
||||||
region: ann_source.region(),
|
region: ann_source.region(),
|
||||||
},
|
},
|
||||||
|
@ -559,7 +559,7 @@ pub fn constrain_expr<'a>(
|
||||||
name,
|
name,
|
||||||
arity,
|
arity,
|
||||||
AnnotationSource::TypedIfBranch {
|
AnnotationSource::TypedIfBranch {
|
||||||
index: Index::zero_based(branches.len()),
|
index: HumanIndex::zero_based(branches.len()),
|
||||||
num_branches,
|
num_branches,
|
||||||
region: ann_source.region(),
|
region: ann_source.region(),
|
||||||
},
|
},
|
||||||
|
@ -596,7 +596,7 @@ pub fn constrain_expr<'a>(
|
||||||
body,
|
body,
|
||||||
Expected::ForReason(
|
Expected::ForReason(
|
||||||
Reason::IfBranch {
|
Reason::IfBranch {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
total_branches: branches.len(),
|
total_branches: branches.len(),
|
||||||
},
|
},
|
||||||
Type2::Variable(*expr_var),
|
Type2::Variable(*expr_var),
|
||||||
|
@ -616,7 +616,7 @@ pub fn constrain_expr<'a>(
|
||||||
final_else_expr,
|
final_else_expr,
|
||||||
Expected::ForReason(
|
Expected::ForReason(
|
||||||
Reason::IfBranch {
|
Reason::IfBranch {
|
||||||
index: Index::zero_based(branches.len()),
|
index: HumanIndex::zero_based(branches.len()),
|
||||||
total_branches: branches.len() + 1,
|
total_branches: branches.len() + 1,
|
||||||
},
|
},
|
||||||
Type2::Variable(*expr_var),
|
Type2::Variable(*expr_var),
|
||||||
|
@ -691,7 +691,7 @@ pub fn constrain_expr<'a>(
|
||||||
when_branch,
|
when_branch,
|
||||||
PExpected::ForReason(
|
PExpected::ForReason(
|
||||||
PReason::WhenMatch {
|
PReason::WhenMatch {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
},
|
},
|
||||||
cond_type.shallow_clone(),
|
cond_type.shallow_clone(),
|
||||||
pattern_region,
|
pattern_region,
|
||||||
|
@ -700,7 +700,7 @@ pub fn constrain_expr<'a>(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
*arity,
|
*arity,
|
||||||
AnnotationSource::TypedWhenBranch {
|
AnnotationSource::TypedWhenBranch {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
region: ann_source.region(),
|
region: ann_source.region(),
|
||||||
},
|
},
|
||||||
typ.shallow_clone(),
|
typ.shallow_clone(),
|
||||||
|
@ -733,14 +733,14 @@ pub fn constrain_expr<'a>(
|
||||||
when_branch,
|
when_branch,
|
||||||
PExpected::ForReason(
|
PExpected::ForReason(
|
||||||
PReason::WhenMatch {
|
PReason::WhenMatch {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
},
|
},
|
||||||
cond_type.shallow_clone(),
|
cond_type.shallow_clone(),
|
||||||
pattern_region,
|
pattern_region,
|
||||||
),
|
),
|
||||||
Expected::ForReason(
|
Expected::ForReason(
|
||||||
Reason::WhenBranch {
|
Reason::WhenBranch {
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
},
|
},
|
||||||
branch_type.shallow_clone(),
|
branch_type.shallow_clone(),
|
||||||
// TODO: when_branch.value.region,
|
// TODO: when_branch.value.region,
|
||||||
|
@ -1065,7 +1065,7 @@ pub fn constrain_expr<'a>(
|
||||||
|
|
||||||
let reason = Reason::LowLevelOpArg {
|
let reason = Reason::LowLevelOpArg {
|
||||||
op: *op,
|
op: *op,
|
||||||
arg_index: Index::zero_based(index),
|
arg_index: HumanIndex::zero_based(index),
|
||||||
};
|
};
|
||||||
let expected_arg =
|
let expected_arg =
|
||||||
Expected::ForReason(reason, arg_type.shallow_clone(), Region::zero());
|
Expected::ForReason(reason, arg_type.shallow_clone(), Region::zero());
|
||||||
|
@ -1681,7 +1681,7 @@ fn constrain_tag_pattern<'a>(
|
||||||
let expected = PExpected::ForReason(
|
let expected = PExpected::ForReason(
|
||||||
PReason::TagArg {
|
PReason::TagArg {
|
||||||
tag_name: tag_name.clone(),
|
tag_name: tag_name.clone(),
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
},
|
},
|
||||||
pattern_type,
|
pattern_type,
|
||||||
region,
|
region,
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
use bumpalo::collections::Vec;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use roc_fmt::def::fmt_def;
|
|
||||||
use roc_fmt::module::fmt_module;
|
|
||||||
use roc_parse::ast::{Def, Module};
|
|
||||||
use roc_parse::module::module_defs;
|
|
||||||
use roc_parse::parser;
|
|
||||||
use roc_parse::parser::{Parser, SyntaxError};
|
|
||||||
use roc_region::all::Located;
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::{fs, io};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct File<'a> {
|
|
||||||
path: &'a Path,
|
|
||||||
module_header: Module<'a>,
|
|
||||||
content: Vec<'a, Located<Def<'a>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ReadError<'a> {
|
|
||||||
Read(std::io::Error),
|
|
||||||
ParseDefs(SyntaxError<'a>),
|
|
||||||
ParseHeader(SyntaxError<'a>),
|
|
||||||
DoesntHaveRocExtension,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> File<'a> {
|
|
||||||
pub fn read(path: &'a Path, arena: &'a Bump) -> Result<File<'a>, ReadError<'a>> {
|
|
||||||
if path.extension() != Some(OsStr::new("roc")) {
|
|
||||||
return Err(ReadError::DoesntHaveRocExtension);
|
|
||||||
}
|
|
||||||
|
|
||||||
let bytes = fs::read(path).map_err(ReadError::Read)?;
|
|
||||||
|
|
||||||
let allocation = arena.alloc(bytes);
|
|
||||||
|
|
||||||
let module_parse_state = parser::State::new(allocation);
|
|
||||||
let parsed_module = roc_parse::module::parse_header(arena, module_parse_state);
|
|
||||||
|
|
||||||
match parsed_module {
|
|
||||||
Ok((module, state)) => {
|
|
||||||
let parsed_defs = module_defs().parse(arena, state);
|
|
||||||
|
|
||||||
match parsed_defs {
|
|
||||||
Ok((_, defs, _)) => Ok(File {
|
|
||||||
path,
|
|
||||||
module_header: module,
|
|
||||||
content: defs,
|
|
||||||
}),
|
|
||||||
Err((_, error, _)) => Err(ReadError::ParseDefs(error)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => Err(ReadError::ParseHeader(SyntaxError::Header(error))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fmt(&self) -> String {
|
|
||||||
let arena = Bump::new();
|
|
||||||
let mut formatted_file = String::new();
|
|
||||||
|
|
||||||
let mut module_header_buf = bumpalo::collections::String::new_in(&arena);
|
|
||||||
fmt_module(&mut module_header_buf, &self.module_header);
|
|
||||||
|
|
||||||
formatted_file.push_str(module_header_buf.as_str());
|
|
||||||
|
|
||||||
for def in &self.content {
|
|
||||||
let mut def_buf = bumpalo::collections::String::new_in(&arena);
|
|
||||||
|
|
||||||
fmt_def(&mut def_buf, &def.value, 0);
|
|
||||||
|
|
||||||
formatted_file.push_str(def_buf.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
formatted_file
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fmt_then_write_to(&self, write_path: &'a Path) -> io::Result<()> {
|
|
||||||
let formatted_file = self.fmt();
|
|
||||||
|
|
||||||
fs::write(write_path, formatted_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fmt_then_write_with_name(&self, new_name: &str) -> io::Result<()> {
|
|
||||||
self.fmt_then_write_to(
|
|
||||||
self.path
|
|
||||||
.with_file_name(new_name)
|
|
||||||
.with_extension("roc")
|
|
||||||
.as_path(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fmt_then_write(&self) -> io::Result<()> {
|
|
||||||
self.fmt_then_write_to(self.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test_file {
|
|
||||||
use crate::lang::roc_file;
|
|
||||||
use bumpalo::Bump;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_and_fmt_simple_roc_module() {
|
|
||||||
let simple_module_path = Path::new("./tests/modules/SimpleUnformatted.roc");
|
|
||||||
|
|
||||||
let arena = Bump::new();
|
|
||||||
|
|
||||||
let file = roc_file::File::read(simple_module_path, &arena)
|
|
||||||
.expect("Could not read SimpleUnformatted.roc in test_file test");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
file.fmt(),
|
|
||||||
indoc!(
|
|
||||||
r#"
|
|
||||||
interface Simple
|
|
||||||
exposes [
|
|
||||||
v, x
|
|
||||||
]
|
|
||||||
imports []
|
|
||||||
|
|
||||||
v : Str
|
|
||||||
|
|
||||||
v = "Value!"
|
|
||||||
|
|
||||||
x : Int
|
|
||||||
x = 4"#
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -180,6 +180,8 @@ fn fmt_all<'a>(arena: &'a Bump, buf: &mut Buf<'a>, ast: &'a Ast) {
|
||||||
for def in &ast.defs {
|
for def in &ast.defs {
|
||||||
fmt_def(buf, arena.alloc(def.value), 0);
|
fmt_def(buf, arena.alloc(def.value), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buf.fmt_end_of_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
|
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub const FLAG_DEV: &str = "dev";
|
||||||
pub const FLAG_OPTIMIZE: &str = "optimize";
|
pub const FLAG_OPTIMIZE: &str = "optimize";
|
||||||
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
pub const FLAG_OPT_SIZE: &str = "opt-size";
|
||||||
pub const FLAG_LIB: &str = "lib";
|
pub const FLAG_LIB: &str = "lib";
|
||||||
pub const FLAG_BACKEND: &str = "backend";
|
pub const FLAG_TARGET: &str = "target";
|
||||||
pub const FLAG_TIME: &str = "time";
|
pub const FLAG_TIME: &str = "time";
|
||||||
pub const FLAG_LINK: &str = "roc-linker";
|
pub const FLAG_LINK: &str = "roc-linker";
|
||||||
pub const FLAG_PRECOMPILED: &str = "precompiled-host";
|
pub const FLAG_PRECOMPILED: &str = "precompiled-host";
|
||||||
|
@ -42,7 +42,6 @@ pub const FLAG_VALGRIND: &str = "valgrind";
|
||||||
pub const FLAG_CHECK: &str = "check";
|
pub const FLAG_CHECK: &str = "check";
|
||||||
pub const ROC_FILE: &str = "ROC_FILE";
|
pub const ROC_FILE: &str = "ROC_FILE";
|
||||||
pub const ROC_DIR: &str = "ROC_DIR";
|
pub const ROC_DIR: &str = "ROC_DIR";
|
||||||
pub const BACKEND: &str = "BACKEND";
|
|
||||||
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
pub const DIRECTORY_OR_FILES: &str = "DIRECTORY_OR_FILES";
|
||||||
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
pub const ARGS_FOR_APP: &str = "ARGS_FOR_APP";
|
||||||
|
|
||||||
|
@ -76,12 +75,11 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
.required(false),
|
.required(false),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(FLAG_BACKEND)
|
Arg::new(FLAG_TARGET)
|
||||||
.long(FLAG_BACKEND)
|
.long(FLAG_TARGET)
|
||||||
.about("Choose a different backend")
|
.about("Choose a different target")
|
||||||
// .requires(BACKEND)
|
.default_value(Target::default().as_str())
|
||||||
.default_value(Backend::default().as_str())
|
.possible_values(Target::OPTIONS)
|
||||||
.possible_values(Backend::OPTIONS)
|
|
||||||
.required(false),
|
.required(false),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -212,12 +210,11 @@ pub fn build_app<'a>() -> App<'a> {
|
||||||
.required(false),
|
.required(false),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(FLAG_BACKEND)
|
Arg::new(FLAG_TARGET)
|
||||||
.long(FLAG_BACKEND)
|
.long(FLAG_TARGET)
|
||||||
.about("Choose a different backend")
|
.about("Choose a different target")
|
||||||
// .requires(BACKEND)
|
.default_value(Target::default().as_str())
|
||||||
.default_value(Backend::default().as_str())
|
.possible_values(Target::OPTIONS)
|
||||||
.possible_values(Backend::OPTIONS)
|
|
||||||
.required(false),
|
.required(false),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -273,12 +270,12 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use BuildConfig::*;
|
use BuildConfig::*;
|
||||||
|
|
||||||
let backend = match matches.value_of(FLAG_BACKEND) {
|
let target = match matches.value_of(FLAG_TARGET) {
|
||||||
Some(name) => Backend::from_str(name).unwrap(),
|
Some(name) => Target::from_str(name).unwrap(),
|
||||||
None => Backend::default(),
|
None => Target::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let target = backend.to_triple();
|
let triple = target.to_triple();
|
||||||
|
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let filename = matches.value_of(ROC_FILE).unwrap();
|
let filename = matches.value_of(ROC_FILE).unwrap();
|
||||||
|
@ -306,10 +303,10 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
let surgically_link = matches.is_present(FLAG_LINK);
|
let surgically_link = matches.is_present(FLAG_LINK);
|
||||||
let precompiled = matches.is_present(FLAG_PRECOMPILED);
|
let precompiled = matches.is_present(FLAG_PRECOMPILED);
|
||||||
|
|
||||||
if surgically_link && !roc_linker::supported(&link_type, &target) {
|
if surgically_link && !roc_linker::supported(&link_type, &triple) {
|
||||||
panic!(
|
panic!(
|
||||||
"Link type, {:?}, with target, {}, not supported by roc linker",
|
"Link type, {:?}, with target, {}, not supported by roc linker",
|
||||||
link_type, target
|
link_type, triple
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +335,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
let target_valgrind = matches.is_present(FLAG_VALGRIND);
|
let target_valgrind = matches.is_present(FLAG_VALGRIND);
|
||||||
let res_binary_path = build_file(
|
let res_binary_path = build_file(
|
||||||
&arena,
|
&arena,
|
||||||
&target,
|
&triple,
|
||||||
src_dir,
|
src_dir,
|
||||||
path,
|
path,
|
||||||
opt_level,
|
opt_level,
|
||||||
|
@ -377,7 +374,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
Ok(outcome.status_code())
|
Ok(outcome.status_code())
|
||||||
}
|
}
|
||||||
BuildAndRun { roc_file_arg_index } => {
|
BuildAndRun { roc_file_arg_index } => {
|
||||||
let mut cmd = match target.architecture {
|
let mut cmd = match triple.architecture {
|
||||||
Architecture::Wasm32 => {
|
Architecture::Wasm32 => {
|
||||||
// If possible, report the generated executable name relative to the current dir.
|
// If possible, report the generated executable name relative to the current dir.
|
||||||
let generated_filename = binary_path
|
let generated_filename = binary_path
|
||||||
|
@ -398,7 +395,7 @@ pub fn build(matches: &ArgMatches, config: BuildConfig) -> io::Result<i32> {
|
||||||
_ => Command::new(&binary_path),
|
_ => Command::new(&binary_path),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Architecture::Wasm32 = target.architecture {
|
if let Architecture::Wasm32 = triple.architecture {
|
||||||
cmd.arg(binary_path);
|
cmd.arg(binary_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,43 +500,43 @@ fn run_with_wasmer(_wasm_path: &std::path::Path, _args: &[String]) {
|
||||||
println!("Running wasm files not support");
|
println!("Running wasm files not support");
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Backend {
|
enum Target {
|
||||||
Host,
|
Host,
|
||||||
X86_32,
|
X86_32,
|
||||||
X86_64,
|
X86_64,
|
||||||
Wasm32,
|
Wasm32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Backend {
|
impl Default for Target {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Backend::Host
|
Target::Host
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Target {
|
||||||
const fn as_str(&self) -> &'static str {
|
const fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Backend::Host => "host",
|
Target::Host => "host",
|
||||||
Backend::X86_32 => "x86_32",
|
Target::X86_32 => "x86_32",
|
||||||
Backend::X86_64 => "x86_64",
|
Target::X86_64 => "x86_64",
|
||||||
Backend::Wasm32 => "wasm32",
|
Target::Wasm32 => "wasm32",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NOTE keep up to date!
|
/// NOTE keep up to date!
|
||||||
const OPTIONS: &'static [&'static str] = &[
|
const OPTIONS: &'static [&'static str] = &[
|
||||||
Backend::Host.as_str(),
|
Target::Host.as_str(),
|
||||||
Backend::X86_32.as_str(),
|
Target::X86_32.as_str(),
|
||||||
Backend::X86_64.as_str(),
|
Target::X86_64.as_str(),
|
||||||
Backend::Wasm32.as_str(),
|
Target::Wasm32.as_str(),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn to_triple(&self) -> Triple {
|
fn to_triple(&self) -> Triple {
|
||||||
let mut triple = Triple::unknown();
|
let mut triple = Triple::unknown();
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Backend::Host => Triple::host(),
|
Target::Host => Triple::host(),
|
||||||
Backend::X86_32 => {
|
Target::X86_32 => {
|
||||||
triple.architecture = Architecture::X86_32(X86_32Architecture::I386);
|
triple.architecture = Architecture::X86_32(X86_32Architecture::I386);
|
||||||
triple.binary_format = BinaryFormat::Elf;
|
triple.binary_format = BinaryFormat::Elf;
|
||||||
|
|
||||||
|
@ -548,13 +545,13 @@ impl Backend {
|
||||||
|
|
||||||
triple
|
triple
|
||||||
}
|
}
|
||||||
Backend::X86_64 => {
|
Target::X86_64 => {
|
||||||
triple.architecture = Architecture::X86_64;
|
triple.architecture = Architecture::X86_64;
|
||||||
triple.binary_format = BinaryFormat::Elf;
|
triple.binary_format = BinaryFormat::Elf;
|
||||||
|
|
||||||
triple
|
triple
|
||||||
}
|
}
|
||||||
Backend::Wasm32 => {
|
Target::Wasm32 => {
|
||||||
triple.architecture = Architecture::Wasm32;
|
triple.architecture = Architecture::Wasm32;
|
||||||
triple.binary_format = BinaryFormat::Wasm;
|
triple.binary_format = BinaryFormat::Wasm;
|
||||||
|
|
||||||
|
@ -564,21 +561,21 @@ impl Backend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Backend {
|
impl std::fmt::Display for Target {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.as_str())
|
write!(f, "{}", self.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for Backend {
|
impl std::str::FromStr for Target {
|
||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"host" => Ok(Backend::Host),
|
"host" => Ok(Target::Host),
|
||||||
"x86_32" => Ok(Backend::X86_32),
|
"x86_32" => Ok(Target::X86_32),
|
||||||
"x86_64" => Ok(Backend::X86_64),
|
"x86_64" => Ok(Target::X86_64),
|
||||||
"wasm32" => Ok(Backend::Wasm32),
|
"wasm32" => Ok(Target::Wasm32),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,15 +64,15 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) {
|
fn check_compile_error(file: &Path, flags: &[&str], expected: &str) {
|
||||||
let compile_out = run_roc(&[&["check", file.to_str().unwrap()], &flags[..]].concat());
|
let compile_out = run_roc(&[&["check", file.to_str().unwrap()], flags].concat());
|
||||||
let err = compile_out.stdout.trim();
|
let err = compile_out.stdout.trim();
|
||||||
let err = strip_colors(&err);
|
let err = strip_colors(err);
|
||||||
assert_multiline_str_eq!(err, expected.into());
|
assert_multiline_str_eq!(err, expected.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
|
fn check_format_check_as_expected(file: &Path, expects_success_exit_code: bool) {
|
||||||
let flags = &["--check"];
|
let flags = &["--check"];
|
||||||
let out = run_roc(&[&["format", &file.to_str().unwrap()], &flags[..]].concat());
|
let out = run_roc(&[&["format", file.to_str().unwrap()], &flags[..]].concat());
|
||||||
|
|
||||||
if expects_success_exit_code {
|
if expects_success_exit_code {
|
||||||
assert!(out.status.success());
|
assert!(out.status.success());
|
||||||
|
@ -194,7 +194,7 @@ mod cli_run {
|
||||||
) {
|
) {
|
||||||
assert_eq!(input_file, None, "Wasm does not support input files");
|
assert_eq!(input_file, None, "Wasm does not support input files");
|
||||||
let mut flags = flags.to_vec();
|
let mut flags = flags.to_vec();
|
||||||
flags.push("--backend=wasm32");
|
flags.push("--target=wasm32");
|
||||||
|
|
||||||
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
let compile_out = run_roc(&[&["build", file.to_str().unwrap()], flags.as_slice()].concat());
|
||||||
if !compile_out.stderr.is_empty() {
|
if !compile_out.stderr.is_empty() {
|
||||||
|
@ -565,7 +565,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
benchmark.stdin,
|
benchmark.stdin,
|
||||||
benchmark.executable_filename,
|
benchmark.executable_filename,
|
||||||
&["--backend=x86_32"],
|
&["--target=x86_32"],
|
||||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||||
benchmark.expected_ending,
|
benchmark.expected_ending,
|
||||||
benchmark.use_valgrind,
|
benchmark.use_valgrind,
|
||||||
|
@ -575,7 +575,7 @@ mod cli_run {
|
||||||
&file_name,
|
&file_name,
|
||||||
benchmark.stdin,
|
benchmark.stdin,
|
||||||
benchmark.executable_filename,
|
benchmark.executable_filename,
|
||||||
&["--backend=x86_32", "--optimize"],
|
&["--target=x86_32", "--optimize"],
|
||||||
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
benchmark.input_file.and_then(|file| Some(examples_dir("benchmarks").join(file))),
|
||||||
benchmark.expected_ending,
|
benchmark.expected_ending,
|
||||||
benchmark.use_valgrind,
|
benchmark.use_valgrind,
|
||||||
|
|
|
@ -61,15 +61,15 @@ pub export fn main() i32 {
|
||||||
// actually call roc to populate the callresult
|
// actually call roc to populate the callresult
|
||||||
const callresult = roc__mainForHost_1_exposed();
|
const callresult = roc__mainForHost_1_exposed();
|
||||||
|
|
||||||
|
// end time
|
||||||
|
var ts2: std.os.timespec = undefined;
|
||||||
|
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
||||||
|
|
||||||
// stdout the result
|
// stdout the result
|
||||||
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
||||||
|
|
||||||
callresult.deinit();
|
callresult.deinit();
|
||||||
|
|
||||||
// end time
|
|
||||||
var ts2: std.os.timespec = undefined;
|
|
||||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
|
||||||
|
|
||||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||||
|
|
||||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||||
|
|
|
@ -60,15 +60,15 @@ pub export fn main() i32 {
|
||||||
// actually call roc to populate the callresult
|
// actually call roc to populate the callresult
|
||||||
const callresult = roc__mainForHost_1_exposed();
|
const callresult = roc__mainForHost_1_exposed();
|
||||||
|
|
||||||
|
// end time
|
||||||
|
var ts2: std.os.timespec = undefined;
|
||||||
|
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
||||||
|
|
||||||
// stdout the result
|
// stdout the result
|
||||||
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
||||||
|
|
||||||
callresult.deinit();
|
callresult.deinit();
|
||||||
|
|
||||||
// end time
|
|
||||||
var ts2: std.os.timespec = undefined;
|
|
||||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
|
||||||
|
|
||||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||||
|
|
||||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||||
|
|
|
@ -16,6 +16,7 @@ roc_types = { path = "../types" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
ven_graph = { path = "../../vendor/pathfinding" }
|
ven_graph = { path = "../../vendor/pathfinding" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
|
|
|
@ -29,6 +29,8 @@ pub struct IntroducedVariables {
|
||||||
// but a variable can only have one name. Therefore
|
// but a variable can only have one name. Therefore
|
||||||
// `ftv : SendMap<Variable, Lowercase>`.
|
// `ftv : SendMap<Variable, Lowercase>`.
|
||||||
pub wildcards: Vec<Variable>,
|
pub wildcards: Vec<Variable>,
|
||||||
|
pub lambda_sets: Vec<Variable>,
|
||||||
|
pub inferred: Vec<Variable>,
|
||||||
pub var_by_name: SendMap<Lowercase, Variable>,
|
pub var_by_name: SendMap<Lowercase, Variable>,
|
||||||
pub name_by_var: SendMap<Variable, Lowercase>,
|
pub name_by_var: SendMap<Variable, Lowercase>,
|
||||||
pub host_exposed_aliases: MutMap<Symbol, Variable>,
|
pub host_exposed_aliases: MutMap<Symbol, Variable>,
|
||||||
|
@ -44,12 +46,22 @@ impl IntroducedVariables {
|
||||||
self.wildcards.push(var);
|
self.wildcards.push(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn insert_inferred(&mut self, var: Variable) {
|
||||||
|
self.inferred.push(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_lambda_set(&mut self, var: Variable) {
|
||||||
|
self.lambda_sets.push(var);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert_host_exposed_alias(&mut self, symbol: Symbol, var: Variable) {
|
pub fn insert_host_exposed_alias(&mut self, symbol: Symbol, var: Variable) {
|
||||||
self.host_exposed_aliases.insert(symbol, var);
|
self.host_exposed_aliases.insert(symbol, var);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn union(&mut self, other: &Self) {
|
pub fn union(&mut self, other: &Self) {
|
||||||
self.wildcards.extend(other.wildcards.iter().cloned());
|
self.wildcards.extend(other.wildcards.iter().cloned());
|
||||||
|
self.lambda_sets.extend(other.lambda_sets.iter().cloned());
|
||||||
|
self.inferred.extend(other.inferred.iter().cloned());
|
||||||
self.var_by_name.extend(other.var_by_name.clone());
|
self.var_by_name.extend(other.var_by_name.clone());
|
||||||
self.name_by_var.extend(other.name_by_var.clone());
|
self.name_by_var.extend(other.name_by_var.clone());
|
||||||
self.host_exposed_aliases
|
self.host_exposed_aliases
|
||||||
|
@ -280,7 +292,9 @@ fn can_annotation_help(
|
||||||
references,
|
references,
|
||||||
);
|
);
|
||||||
|
|
||||||
let closure = Type::Variable(var_store.fresh());
|
let lambda_set = var_store.fresh();
|
||||||
|
introduced_variables.insert_lambda_set(lambda_set);
|
||||||
|
let closure = Type::Variable(lambda_set);
|
||||||
|
|
||||||
Type::Function(args, Box::new(closure), Box::new(ret))
|
Type::Function(args, Box::new(closure), Box::new(ret))
|
||||||
}
|
}
|
||||||
|
@ -326,6 +340,7 @@ fn can_annotation_help(
|
||||||
let (type_arguments, lambda_set_variables, actual) =
|
let (type_arguments, lambda_set_variables, actual) =
|
||||||
instantiate_and_freshen_alias_type(
|
instantiate_and_freshen_alias_type(
|
||||||
var_store,
|
var_store,
|
||||||
|
introduced_variables,
|
||||||
&alias.type_variables,
|
&alias.type_variables,
|
||||||
args,
|
args,
|
||||||
&alias.lambda_set_variables,
|
&alias.lambda_set_variables,
|
||||||
|
@ -505,19 +520,16 @@ fn can_annotation_help(
|
||||||
}
|
}
|
||||||
|
|
||||||
Record { fields, ext } => {
|
Record { fields, ext } => {
|
||||||
let ext_type = match ext {
|
let ext_type = can_extension_type(
|
||||||
Some(loc_ann) => can_annotation_help(
|
|
||||||
env,
|
env,
|
||||||
&loc_ann.value,
|
|
||||||
region,
|
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
local_aliases,
|
local_aliases,
|
||||||
references,
|
references,
|
||||||
),
|
ext,
|
||||||
None => Type::EmptyRec,
|
roc_problem::can::ExtensionTypeKind::Record,
|
||||||
};
|
);
|
||||||
|
|
||||||
if fields.is_empty() {
|
if fields.is_empty() {
|
||||||
match ext {
|
match ext {
|
||||||
|
@ -546,19 +558,16 @@ fn can_annotation_help(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TagUnion { tags, ext, .. } => {
|
TagUnion { tags, ext, .. } => {
|
||||||
let ext_type = match ext {
|
let ext_type = can_extension_type(
|
||||||
Some(loc_ann) => can_annotation_help(
|
|
||||||
env,
|
env,
|
||||||
&loc_ann.value,
|
|
||||||
loc_ann.region,
|
|
||||||
scope,
|
scope,
|
||||||
var_store,
|
var_store,
|
||||||
introduced_variables,
|
introduced_variables,
|
||||||
local_aliases,
|
local_aliases,
|
||||||
references,
|
references,
|
||||||
),
|
ext,
|
||||||
None => Type::EmptyTagUnion,
|
roc_problem::can::ExtensionTypeKind::TagUnion,
|
||||||
};
|
);
|
||||||
|
|
||||||
if tags.is_empty() {
|
if tags.is_empty() {
|
||||||
match ext {
|
match ext {
|
||||||
|
@ -612,6 +621,9 @@ fn can_annotation_help(
|
||||||
// Inference variables aren't bound to a rigid or a wildcard, so all we have to do is
|
// Inference variables aren't bound to a rigid or a wildcard, so all we have to do is
|
||||||
// make a fresh unconstrained variable, and let the type solver fill it in for us 🤠
|
// make a fresh unconstrained variable, and let the type solver fill it in for us 🤠
|
||||||
let var = var_store.fresh();
|
let var = var_store.fresh();
|
||||||
|
|
||||||
|
introduced_variables.insert_inferred(var);
|
||||||
|
|
||||||
Type::Variable(var)
|
Type::Variable(var)
|
||||||
}
|
}
|
||||||
Malformed(string) => {
|
Malformed(string) => {
|
||||||
|
@ -626,8 +638,77 @@ fn can_annotation_help(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn can_extension_type<'a>(
|
||||||
|
env: &mut Env,
|
||||||
|
scope: &mut Scope,
|
||||||
|
var_store: &mut VarStore,
|
||||||
|
introduced_variables: &mut IntroducedVariables,
|
||||||
|
local_aliases: &mut SendMap<Symbol, Alias>,
|
||||||
|
references: &mut MutSet<Symbol>,
|
||||||
|
opt_ext: &Option<&Loc<TypeAnnotation<'a>>>,
|
||||||
|
ext_problem_kind: roc_problem::can::ExtensionTypeKind,
|
||||||
|
) -> Type {
|
||||||
|
fn valid_record_ext_type(typ: &Type) -> bool {
|
||||||
|
// Include erroneous types so that we don't overreport errors.
|
||||||
|
matches!(
|
||||||
|
typ,
|
||||||
|
Type::EmptyRec | Type::Record(..) | Type::Variable(..) | Type::Erroneous(..)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn valid_tag_ext_type(typ: &Type) -> bool {
|
||||||
|
matches!(
|
||||||
|
typ,
|
||||||
|
Type::EmptyTagUnion | Type::TagUnion(..) | Type::Variable(..) | Type::Erroneous(..)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
use roc_problem::can::ExtensionTypeKind;
|
||||||
|
|
||||||
|
let (empty_ext_type, valid_extension_type): (_, fn(&Type) -> bool) = match ext_problem_kind {
|
||||||
|
ExtensionTypeKind::Record => (Type::EmptyRec, valid_record_ext_type),
|
||||||
|
ExtensionTypeKind::TagUnion => (Type::EmptyTagUnion, valid_tag_ext_type),
|
||||||
|
};
|
||||||
|
|
||||||
|
match opt_ext {
|
||||||
|
Some(loc_ann) => {
|
||||||
|
let ext_type = can_annotation_help(
|
||||||
|
env,
|
||||||
|
&loc_ann.value,
|
||||||
|
loc_ann.region,
|
||||||
|
scope,
|
||||||
|
var_store,
|
||||||
|
introduced_variables,
|
||||||
|
local_aliases,
|
||||||
|
references,
|
||||||
|
);
|
||||||
|
if valid_extension_type(ext_type.shallow_dealias()) {
|
||||||
|
ext_type
|
||||||
|
} else {
|
||||||
|
// Report an error but mark the extension variable to be inferred
|
||||||
|
// so that we're as permissive as possible.
|
||||||
|
//
|
||||||
|
// THEORY: invalid extension types can appear in this position. Otherwise
|
||||||
|
// they would be caught as errors during unification.
|
||||||
|
env.problem(roc_problem::can::Problem::InvalidExtensionType {
|
||||||
|
region: loc_ann.region,
|
||||||
|
kind: ext_problem_kind,
|
||||||
|
});
|
||||||
|
|
||||||
|
let var = var_store.fresh();
|
||||||
|
|
||||||
|
introduced_variables.insert_inferred(var);
|
||||||
|
|
||||||
|
Type::Variable(var)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => empty_ext_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn instantiate_and_freshen_alias_type(
|
pub fn instantiate_and_freshen_alias_type(
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
|
introduced_variables: &mut IntroducedVariables,
|
||||||
type_variables: &[Loc<(Lowercase, Variable)>],
|
type_variables: &[Loc<(Lowercase, Variable)>],
|
||||||
type_arguments: Vec<Type>,
|
type_arguments: Vec<Type>,
|
||||||
lambda_set_variables: &[LambdaSet],
|
lambda_set_variables: &[LambdaSet],
|
||||||
|
@ -657,6 +738,7 @@ pub fn instantiate_and_freshen_alias_type(
|
||||||
if let Type::Variable(var) = typ.0 {
|
if let Type::Variable(var) = typ.0 {
|
||||||
let fresh = var_store.fresh();
|
let fresh = var_store.fresh();
|
||||||
substitutions.insert(var, Type::Variable(fresh));
|
substitutions.insert(var, Type::Variable(fresh));
|
||||||
|
introduced_variables.insert_lambda_set(fresh);
|
||||||
new_lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
new_lambda_set_variables.push(LambdaSet(Type::Variable(fresh)));
|
||||||
} else {
|
} else {
|
||||||
unreachable!("at this point there should be only vars in there");
|
unreachable!("at this point there should be only vars in there");
|
||||||
|
@ -681,8 +763,12 @@ pub fn freshen_opaque_def(
|
||||||
.map(|_| Type::Variable(var_store.fresh()))
|
.map(|_| Type::Variable(var_store.fresh()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
// TODO this gets ignored; is that a problem
|
||||||
|
let mut introduced_variables = IntroducedVariables::default();
|
||||||
|
|
||||||
instantiate_and_freshen_alias_type(
|
instantiate_and_freshen_alias_type(
|
||||||
var_store,
|
var_store,
|
||||||
|
&mut introduced_variables,
|
||||||
&opaque.type_variables,
|
&opaque.type_variables,
|
||||||
fresh_arguments,
|
fresh_arguments,
|
||||||
&opaque.lambda_set_variables,
|
&opaque.lambda_set_variables,
|
||||||
|
|
|
@ -1,175 +1,505 @@
|
||||||
use crate::expected::{Expected, PExpected};
|
use crate::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{MutSet, SendMap};
|
use roc_collections::soa::{Index, Slice};
|
||||||
use roc_module::{ident::TagName, symbol::Symbol};
|
use roc_module::ident::TagName;
|
||||||
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
use roc_types::subs::Variable;
|
||||||
use roc_types::types::{Category, PatternCategory, Type};
|
use roc_types::types::{Category, PatternCategory, Type};
|
||||||
use roc_types::{subs::Variable, types::VariableDetail};
|
|
||||||
|
|
||||||
/// A presence constraint is an additive constraint that defines the lower bound
|
#[derive(Debug)]
|
||||||
/// of a type. For example, `Present(t1, IncludesTag(A, []))` means that the
|
pub struct Constraints {
|
||||||
/// type `t1` must contain at least the tag `A`. The additive nature of these
|
pub constraints: Vec<Constraint>,
|
||||||
/// constraints makes them behaviorally different from unification-based constraints.
|
pub types: Vec<Type>,
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
pub variables: Vec<Variable>,
|
||||||
pub enum PresenceConstraint {
|
pub loc_symbols: Vec<(Symbol, Region)>,
|
||||||
IncludesTag(TagName, Vec<Type>, Region, PatternCategory),
|
pub let_constraints: Vec<LetConstraint>,
|
||||||
IsOpen,
|
pub categories: Vec<Category>,
|
||||||
Pattern(Region, PatternCategory, PExpected<Type>),
|
pub pattern_categories: Vec<PatternCategory>,
|
||||||
|
pub expectations: Vec<Expected<Type>>,
|
||||||
|
pub pattern_expectations: Vec<PExpected<Type>>,
|
||||||
|
pub includes_tags: Vec<IncludesTag>,
|
||||||
|
pub strings: Vec<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Constraints {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Constraints {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let constraints = Vec::new();
|
||||||
|
let mut types = Vec::new();
|
||||||
|
let variables = Vec::new();
|
||||||
|
let loc_symbols = Vec::new();
|
||||||
|
let let_constraints = Vec::new();
|
||||||
|
let mut categories = Vec::with_capacity(16);
|
||||||
|
let mut pattern_categories = Vec::with_capacity(16);
|
||||||
|
let expectations = Vec::new();
|
||||||
|
let pattern_expectations = Vec::new();
|
||||||
|
let includes_tags = Vec::new();
|
||||||
|
let strings = Vec::new();
|
||||||
|
|
||||||
|
types.extend([Type::EmptyRec, Type::EmptyTagUnion]);
|
||||||
|
|
||||||
|
categories.extend([
|
||||||
|
Category::Record,
|
||||||
|
Category::ForeignCall,
|
||||||
|
Category::OpaqueArg,
|
||||||
|
Category::Lambda,
|
||||||
|
Category::ClosureSize,
|
||||||
|
Category::StrInterpolation,
|
||||||
|
Category::If,
|
||||||
|
Category::When,
|
||||||
|
Category::Float,
|
||||||
|
Category::Int,
|
||||||
|
Category::Num,
|
||||||
|
Category::List,
|
||||||
|
Category::Str,
|
||||||
|
Category::Character,
|
||||||
|
]);
|
||||||
|
|
||||||
|
pattern_categories.extend([
|
||||||
|
PatternCategory::Record,
|
||||||
|
PatternCategory::EmptyRecord,
|
||||||
|
PatternCategory::PatternGuard,
|
||||||
|
PatternCategory::PatternDefault,
|
||||||
|
PatternCategory::Set,
|
||||||
|
PatternCategory::Map,
|
||||||
|
PatternCategory::Str,
|
||||||
|
PatternCategory::Num,
|
||||||
|
PatternCategory::Int,
|
||||||
|
PatternCategory::Float,
|
||||||
|
PatternCategory::Character,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
constraints,
|
||||||
|
types,
|
||||||
|
variables,
|
||||||
|
loc_symbols,
|
||||||
|
let_constraints,
|
||||||
|
categories,
|
||||||
|
pattern_categories,
|
||||||
|
expectations,
|
||||||
|
pattern_expectations,
|
||||||
|
includes_tags,
|
||||||
|
strings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const EMPTY_RECORD: Index<Type> = Index::new(0);
|
||||||
|
pub const EMPTY_TAG_UNION: Index<Type> = Index::new(1);
|
||||||
|
|
||||||
|
pub const CATEGORY_RECORD: Index<Category> = Index::new(0);
|
||||||
|
pub const CATEGORY_FOREIGNCALL: Index<Category> = Index::new(1);
|
||||||
|
pub const CATEGORY_OPAQUEARG: Index<Category> = Index::new(2);
|
||||||
|
pub const CATEGORY_LAMBDA: Index<Category> = Index::new(3);
|
||||||
|
pub const CATEGORY_CLOSURESIZE: Index<Category> = Index::new(4);
|
||||||
|
pub const CATEGORY_STRINTERPOLATION: Index<Category> = Index::new(5);
|
||||||
|
pub const CATEGORY_IF: Index<Category> = Index::new(6);
|
||||||
|
pub const CATEGORY_WHEN: Index<Category> = Index::new(7);
|
||||||
|
pub const CATEGORY_FLOAT: Index<Category> = Index::new(8);
|
||||||
|
pub const CATEGORY_INT: Index<Category> = Index::new(9);
|
||||||
|
pub const CATEGORY_NUM: Index<Category> = Index::new(10);
|
||||||
|
pub const CATEGORY_LIST: Index<Category> = Index::new(11);
|
||||||
|
pub const CATEGORY_STR: Index<Category> = Index::new(12);
|
||||||
|
pub const CATEGORY_CHARACTER: Index<Category> = Index::new(13);
|
||||||
|
|
||||||
|
pub const PCATEGORY_RECORD: Index<PatternCategory> = Index::new(0);
|
||||||
|
pub const PCATEGORY_EMPTYRECORD: Index<PatternCategory> = Index::new(1);
|
||||||
|
pub const PCATEGORY_PATTERNGUARD: Index<PatternCategory> = Index::new(2);
|
||||||
|
pub const PCATEGORY_PATTERNDEFAULT: Index<PatternCategory> = Index::new(3);
|
||||||
|
pub const PCATEGORY_SET: Index<PatternCategory> = Index::new(4);
|
||||||
|
pub const PCATEGORY_MAP: Index<PatternCategory> = Index::new(5);
|
||||||
|
pub const PCATEGORY_STR: Index<PatternCategory> = Index::new(6);
|
||||||
|
pub const PCATEGORY_NUM: Index<PatternCategory> = Index::new(7);
|
||||||
|
pub const PCATEGORY_INT: Index<PatternCategory> = Index::new(8);
|
||||||
|
pub const PCATEGORY_FLOAT: Index<PatternCategory> = Index::new(9);
|
||||||
|
pub const PCATEGORY_CHARACTER: Index<PatternCategory> = Index::new(10);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn push_type(&mut self, typ: Type) -> Index<Type> {
|
||||||
|
match typ {
|
||||||
|
Type::EmptyRec => Self::EMPTY_RECORD,
|
||||||
|
Type::EmptyTagUnion => Self::EMPTY_TAG_UNION,
|
||||||
|
other => Index::push_new(&mut self.types, other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn push_expected_type(&mut self, expected: Expected<Type>) -> Index<Expected<Type>> {
|
||||||
|
Index::push_new(&mut self.expectations, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn push_category(&mut self, category: Category) -> Index<Category> {
|
||||||
|
match category {
|
||||||
|
Category::Record => Self::CATEGORY_RECORD,
|
||||||
|
Category::ForeignCall => Self::CATEGORY_FOREIGNCALL,
|
||||||
|
Category::OpaqueArg => Self::CATEGORY_OPAQUEARG,
|
||||||
|
Category::Lambda => Self::CATEGORY_LAMBDA,
|
||||||
|
Category::ClosureSize => Self::CATEGORY_CLOSURESIZE,
|
||||||
|
Category::StrInterpolation => Self::CATEGORY_STRINTERPOLATION,
|
||||||
|
Category::If => Self::CATEGORY_IF,
|
||||||
|
Category::When => Self::CATEGORY_WHEN,
|
||||||
|
Category::Float => Self::CATEGORY_FLOAT,
|
||||||
|
Category::Int => Self::CATEGORY_INT,
|
||||||
|
Category::Num => Self::CATEGORY_NUM,
|
||||||
|
Category::List => Self::CATEGORY_LIST,
|
||||||
|
Category::Str => Self::CATEGORY_STR,
|
||||||
|
Category::Character => Self::CATEGORY_CHARACTER,
|
||||||
|
other => Index::push_new(&mut self.categories, other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn push_pattern_category(&mut self, category: PatternCategory) -> Index<PatternCategory> {
|
||||||
|
match category {
|
||||||
|
PatternCategory::Record => Self::PCATEGORY_RECORD,
|
||||||
|
PatternCategory::EmptyRecord => Self::PCATEGORY_EMPTYRECORD,
|
||||||
|
PatternCategory::PatternGuard => Self::PCATEGORY_PATTERNGUARD,
|
||||||
|
PatternCategory::PatternDefault => Self::PCATEGORY_PATTERNDEFAULT,
|
||||||
|
PatternCategory::Set => Self::PCATEGORY_SET,
|
||||||
|
PatternCategory::Map => Self::PCATEGORY_MAP,
|
||||||
|
PatternCategory::Str => Self::PCATEGORY_STR,
|
||||||
|
PatternCategory::Num => Self::PCATEGORY_NUM,
|
||||||
|
PatternCategory::Int => Self::PCATEGORY_INT,
|
||||||
|
PatternCategory::Float => Self::PCATEGORY_FLOAT,
|
||||||
|
PatternCategory::Character => Self::PCATEGORY_CHARACTER,
|
||||||
|
other => Index::push_new(&mut self.pattern_categories, other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn equal_types(
|
||||||
|
&mut self,
|
||||||
|
typ: Type,
|
||||||
|
expected: Expected<Type>,
|
||||||
|
category: Category,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint {
|
||||||
|
let type_index = Index::push_new(&mut self.types, typ);
|
||||||
|
let expected_index = Index::push_new(&mut self.expectations, expected);
|
||||||
|
let category_index = Self::push_category(self, category);
|
||||||
|
|
||||||
|
Constraint::Eq(type_index, expected_index, category_index, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn equal_pattern_types(
|
||||||
|
&mut self,
|
||||||
|
typ: Type,
|
||||||
|
expected: PExpected<Type>,
|
||||||
|
category: PatternCategory,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint {
|
||||||
|
let type_index = Index::push_new(&mut self.types, typ);
|
||||||
|
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
||||||
|
let category_index = Self::push_pattern_category(self, category);
|
||||||
|
|
||||||
|
Constraint::Pattern(type_index, expected_index, category_index, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pattern_presence(
|
||||||
|
&mut self,
|
||||||
|
typ: Type,
|
||||||
|
expected: PExpected<Type>,
|
||||||
|
category: PatternCategory,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint {
|
||||||
|
let type_index = Index::push_new(&mut self.types, typ);
|
||||||
|
let expected_index = Index::push_new(&mut self.pattern_expectations, expected);
|
||||||
|
let category_index = Index::push_new(&mut self.pattern_categories, category);
|
||||||
|
|
||||||
|
Constraint::PatternPresence(type_index, expected_index, category_index, region)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_open_type(&mut self, typ: Type) -> Constraint {
|
||||||
|
let type_index = Index::push_new(&mut self.types, typ);
|
||||||
|
|
||||||
|
Constraint::IsOpenType(type_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn includes_tag<I>(
|
||||||
|
&mut self,
|
||||||
|
typ: Type,
|
||||||
|
tag_name: TagName,
|
||||||
|
types: I,
|
||||||
|
category: PatternCategory,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Type>,
|
||||||
|
{
|
||||||
|
let type_index = Index::push_new(&mut self.types, typ);
|
||||||
|
let category_index = Index::push_new(&mut self.pattern_categories, category);
|
||||||
|
let types_slice = Slice::extend_new(&mut self.types, types);
|
||||||
|
|
||||||
|
let includes_tag = IncludesTag {
|
||||||
|
type_index,
|
||||||
|
tag_name,
|
||||||
|
types: types_slice,
|
||||||
|
pattern_category: category_index,
|
||||||
|
region,
|
||||||
|
};
|
||||||
|
|
||||||
|
let includes_tag_index = Index::push_new(&mut self.includes_tags, includes_tag);
|
||||||
|
|
||||||
|
Constraint::IncludesTag(includes_tag_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variable_slice<I>(&mut self, it: I) -> Slice<Variable>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Variable>,
|
||||||
|
{
|
||||||
|
let start = self.variables.len();
|
||||||
|
self.variables.extend(it);
|
||||||
|
let length = self.variables.len() - start;
|
||||||
|
|
||||||
|
Slice::new(start as _, length as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn def_types_slice<I>(&mut self, it: I) -> DefTypes
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (Symbol, Loc<Type>)>,
|
||||||
|
I::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let it = it.into_iter();
|
||||||
|
|
||||||
|
let types_start = self.types.len();
|
||||||
|
let loc_symbols_start = self.loc_symbols.len();
|
||||||
|
|
||||||
|
// because we have an ExactSizeIterator, we can reserve space here
|
||||||
|
let length = it.len();
|
||||||
|
|
||||||
|
self.types.reserve(length);
|
||||||
|
self.loc_symbols.reserve(length);
|
||||||
|
|
||||||
|
for (symbol, loc_type) in it {
|
||||||
|
let Loc { region, value } = loc_type;
|
||||||
|
|
||||||
|
self.types.push(value);
|
||||||
|
self.loc_symbols.push((symbol, region));
|
||||||
|
}
|
||||||
|
|
||||||
|
DefTypes {
|
||||||
|
types: Slice::new(types_start as _, length as _),
|
||||||
|
loc_symbols: Slice::new(loc_symbols_start as _, length as _),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn exists<I>(&mut self, flex_vars: I, defs_constraint: Constraint) -> Constraint
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Variable>,
|
||||||
|
{
|
||||||
|
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||||
|
|
||||||
|
self.constraints.push(defs_constraint);
|
||||||
|
self.constraints.push(Constraint::True);
|
||||||
|
|
||||||
|
let let_contraint = LetConstraint {
|
||||||
|
rigid_vars: Slice::default(),
|
||||||
|
flex_vars: self.variable_slice(flex_vars),
|
||||||
|
def_types: DefTypes::default(),
|
||||||
|
defs_and_ret_constraint,
|
||||||
|
};
|
||||||
|
|
||||||
|
let let_index = Index::new(self.let_constraints.len() as _);
|
||||||
|
self.let_constraints.push(let_contraint);
|
||||||
|
|
||||||
|
Constraint::Let(let_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn exists_many<I, C>(&mut self, flex_vars: I, defs_constraint: C) -> Constraint
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Variable>,
|
||||||
|
C: IntoIterator<Item = Constraint>,
|
||||||
|
C::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let defs_constraint = self.and_constraint(defs_constraint);
|
||||||
|
|
||||||
|
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||||
|
self.constraints.push(defs_constraint);
|
||||||
|
self.constraints.push(Constraint::True);
|
||||||
|
|
||||||
|
let let_contraint = LetConstraint {
|
||||||
|
rigid_vars: Slice::default(),
|
||||||
|
flex_vars: self.variable_slice(flex_vars),
|
||||||
|
def_types: DefTypes::default(),
|
||||||
|
defs_and_ret_constraint,
|
||||||
|
};
|
||||||
|
|
||||||
|
let let_index = Index::new(self.let_constraints.len() as _);
|
||||||
|
self.let_constraints.push(let_contraint);
|
||||||
|
|
||||||
|
Constraint::Let(let_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn let_constraint<I1, I2, I3>(
|
||||||
|
&mut self,
|
||||||
|
rigid_vars: I1,
|
||||||
|
flex_vars: I2,
|
||||||
|
def_types: I3,
|
||||||
|
defs_constraint: Constraint,
|
||||||
|
ret_constraint: Constraint,
|
||||||
|
) -> Constraint
|
||||||
|
where
|
||||||
|
I1: IntoIterator<Item = Variable>,
|
||||||
|
I2: IntoIterator<Item = Variable>,
|
||||||
|
I3: IntoIterator<Item = (Symbol, Loc<Type>)>,
|
||||||
|
I3::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let defs_and_ret_constraint = Index::new(self.constraints.len() as _);
|
||||||
|
|
||||||
|
self.constraints.push(defs_constraint);
|
||||||
|
self.constraints.push(ret_constraint);
|
||||||
|
|
||||||
|
let let_contraint = LetConstraint {
|
||||||
|
rigid_vars: self.variable_slice(rigid_vars),
|
||||||
|
flex_vars: self.variable_slice(flex_vars),
|
||||||
|
def_types: self.def_types_slice(def_types),
|
||||||
|
defs_and_ret_constraint,
|
||||||
|
};
|
||||||
|
|
||||||
|
let let_index = Index::new(self.let_constraints.len() as _);
|
||||||
|
self.let_constraints.push(let_contraint);
|
||||||
|
|
||||||
|
Constraint::Let(let_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn and_constraint<I>(&mut self, constraints: I) -> Constraint
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = Constraint>,
|
||||||
|
I::IntoIter: ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let mut it = constraints.into_iter();
|
||||||
|
|
||||||
|
match it.len() {
|
||||||
|
0 => Constraint::True,
|
||||||
|
1 => it.next().unwrap(),
|
||||||
|
_ => {
|
||||||
|
let start = self.constraints.len() as u32;
|
||||||
|
|
||||||
|
self.constraints.extend(it);
|
||||||
|
|
||||||
|
let end = self.constraints.len() as u32;
|
||||||
|
|
||||||
|
let slice = Slice::new(start, (end - start) as u16);
|
||||||
|
|
||||||
|
Constraint::And(slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup(
|
||||||
|
&mut self,
|
||||||
|
symbol: Symbol,
|
||||||
|
expected: Expected<Type>,
|
||||||
|
region: Region,
|
||||||
|
) -> Constraint {
|
||||||
|
Constraint::Lookup(
|
||||||
|
symbol,
|
||||||
|
Index::push_new(&mut self.expectations, expected),
|
||||||
|
region,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
pub fn contains_save_the_environment(&self, constraint: &Constraint) -> bool {
|
||||||
|
match constraint {
|
||||||
|
Constraint::Eq(..) => false,
|
||||||
|
Constraint::Store(..) => false,
|
||||||
|
Constraint::Lookup(..) => false,
|
||||||
|
Constraint::Pattern(..) => false,
|
||||||
|
Constraint::True => false,
|
||||||
|
Constraint::SaveTheEnvironment => true,
|
||||||
|
Constraint::Let(index) => {
|
||||||
|
let let_constraint = &self.let_constraints[index.index()];
|
||||||
|
|
||||||
|
let offset = let_constraint.defs_and_ret_constraint.index();
|
||||||
|
let defs_constraint = &self.constraints[offset];
|
||||||
|
let ret_constraint = &self.constraints[offset + 1];
|
||||||
|
|
||||||
|
self.contains_save_the_environment(defs_constraint)
|
||||||
|
|| self.contains_save_the_environment(ret_constraint)
|
||||||
|
}
|
||||||
|
Constraint::And(slice) => {
|
||||||
|
let constraints = &self.constraints[slice.indices()];
|
||||||
|
|
||||||
|
constraints
|
||||||
|
.iter()
|
||||||
|
.any(|c| self.contains_save_the_environment(c))
|
||||||
|
}
|
||||||
|
Constraint::IsOpenType(_) => false,
|
||||||
|
Constraint::IncludesTag(_) => false,
|
||||||
|
Constraint::PatternPresence(_, _, _, _) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(
|
||||||
|
&mut self,
|
||||||
|
typ: Type,
|
||||||
|
variable: Variable,
|
||||||
|
filename: &'static str,
|
||||||
|
line_number: u32,
|
||||||
|
) -> Constraint {
|
||||||
|
let type_index = Index::push_new(&mut self.types, typ);
|
||||||
|
let string_index = Index::push_new(&mut self.strings, filename);
|
||||||
|
|
||||||
|
Constraint::Store(type_index, variable, string_index, line_number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::assert_eq_size!([u8; 3 * 8], Constraint);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Constraint {
|
pub enum Constraint {
|
||||||
Eq(Type, Expected<Type>, Category, Region),
|
Eq(Index<Type>, Index<Expected<Type>>, Index<Category>, Region),
|
||||||
Store(Type, Variable, &'static str, u32),
|
Store(Index<Type>, Variable, Index<&'static str>, u32),
|
||||||
Lookup(Symbol, Expected<Type>, Region),
|
Lookup(Symbol, Index<Expected<Type>>, Region),
|
||||||
Pattern(Region, PatternCategory, Type, PExpected<Type>),
|
Pattern(
|
||||||
|
Index<Type>,
|
||||||
|
Index<PExpected<Type>>,
|
||||||
|
Index<PatternCategory>,
|
||||||
|
Region,
|
||||||
|
),
|
||||||
True, // Used for things that always unify, e.g. blanks and runtime errors
|
True, // Used for things that always unify, e.g. blanks and runtime errors
|
||||||
SaveTheEnvironment,
|
SaveTheEnvironment,
|
||||||
Let(Box<LetConstraint>),
|
Let(Index<LetConstraint>),
|
||||||
And(Vec<Constraint>),
|
And(Slice<Constraint>),
|
||||||
Present(Type, PresenceConstraint),
|
/// Presence constraints
|
||||||
|
IsOpenType(Index<Type>), // Theory; always applied to a variable? if yes the use that
|
||||||
|
IncludesTag(Index<IncludesTag>),
|
||||||
|
PatternPresence(
|
||||||
|
Index<Type>,
|
||||||
|
Index<PExpected<Type>>,
|
||||||
|
Index<PatternCategory>,
|
||||||
|
Region,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||||
|
pub struct DefTypes {
|
||||||
|
pub types: Slice<Type>,
|
||||||
|
pub loc_symbols: Slice<(Symbol, Region)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct LetConstraint {
|
pub struct LetConstraint {
|
||||||
pub rigid_vars: Vec<Variable>,
|
pub rigid_vars: Slice<Variable>,
|
||||||
pub flex_vars: Vec<Variable>,
|
pub flex_vars: Slice<Variable>,
|
||||||
pub def_types: SendMap<Symbol, Loc<Type>>,
|
pub def_types: DefTypes,
|
||||||
pub defs_constraint: Constraint,
|
pub defs_and_ret_constraint: Index<(Constraint, Constraint)>,
|
||||||
pub ret_constraint: Constraint,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VALIDATE
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct IncludesTag {
|
||||||
#[derive(Default, Clone)]
|
pub type_index: Index<Type>,
|
||||||
struct Declared {
|
pub tag_name: TagName,
|
||||||
pub rigid_vars: MutSet<Variable>,
|
pub types: Slice<Type>,
|
||||||
pub flex_vars: MutSet<Variable>,
|
pub pattern_category: Index<PatternCategory>,
|
||||||
}
|
pub region: Region,
|
||||||
|
|
||||||
impl Constraint {
|
|
||||||
pub fn validate(&self) -> bool {
|
|
||||||
let mut unbound = Default::default();
|
|
||||||
|
|
||||||
validate_help(self, &Declared::default(), &mut unbound);
|
|
||||||
|
|
||||||
if !unbound.type_variables.is_empty() {
|
|
||||||
panic!("found unbound type variables {:?}", &unbound.type_variables);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unbound.lambda_set_variables.is_empty() {
|
|
||||||
panic!(
|
|
||||||
"found unbound lambda set variables {:?}",
|
|
||||||
&unbound.lambda_set_variables
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !unbound.recursion_variables.is_empty() {
|
|
||||||
panic!(
|
|
||||||
"found unbound recursion variables {:?}",
|
|
||||||
&unbound.recursion_variables
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains_save_the_environment(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Constraint::Eq(_, _, _, _) => false,
|
|
||||||
Constraint::Store(_, _, _, _) => false,
|
|
||||||
Constraint::Lookup(_, _, _) => false,
|
|
||||||
Constraint::Pattern(_, _, _, _) => false,
|
|
||||||
Constraint::True => false,
|
|
||||||
Constraint::SaveTheEnvironment => true,
|
|
||||||
Constraint::Let(boxed) => {
|
|
||||||
boxed.ret_constraint.contains_save_the_environment()
|
|
||||||
|| boxed.defs_constraint.contains_save_the_environment()
|
|
||||||
}
|
|
||||||
Constraint::And(cs) => cs.iter().any(|c| c.contains_save_the_environment()),
|
|
||||||
Constraint::Present(_, _) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subtract(declared: &Declared, detail: &VariableDetail, accum: &mut VariableDetail) {
|
|
||||||
for var in &detail.type_variables {
|
|
||||||
if !(declared.rigid_vars.contains(var) || declared.flex_vars.contains(var)) {
|
|
||||||
accum.type_variables.insert(*var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lambda set variables are always flex
|
|
||||||
for var in &detail.lambda_set_variables {
|
|
||||||
if declared.rigid_vars.contains(var) {
|
|
||||||
panic!("lambda set variable {:?} is declared as rigid", var);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !declared.flex_vars.contains(var) {
|
|
||||||
accum.lambda_set_variables.push(*var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// recursion vars should be always rigid
|
|
||||||
for var in &detail.recursion_variables {
|
|
||||||
if declared.flex_vars.contains(var) {
|
|
||||||
panic!("recursion variable {:?} is declared as flex", var);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !declared.rigid_vars.contains(var) {
|
|
||||||
accum.recursion_variables.insert(*var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_help(constraint: &Constraint, declared: &Declared, accum: &mut VariableDetail) {
|
|
||||||
use Constraint::*;
|
|
||||||
|
|
||||||
match constraint {
|
|
||||||
True | SaveTheEnvironment | Lookup(_, _, _) => { /* nothing */ }
|
|
||||||
Store(typ, var, _, _) => {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
|
|
||||||
if !declared.flex_vars.contains(var) {
|
|
||||||
accum.type_variables.insert(*var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Constraint::Eq(typ, expected, _, _) => {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
|
|
||||||
}
|
|
||||||
Constraint::Pattern(_, _, typ, expected) => {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
|
|
||||||
}
|
|
||||||
Constraint::Let(letcon) => {
|
|
||||||
let mut declared = declared.clone();
|
|
||||||
declared
|
|
||||||
.rigid_vars
|
|
||||||
.extend(letcon.rigid_vars.iter().copied());
|
|
||||||
declared.flex_vars.extend(letcon.flex_vars.iter().copied());
|
|
||||||
|
|
||||||
validate_help(&letcon.defs_constraint, &declared, accum);
|
|
||||||
validate_help(&letcon.ret_constraint, &declared, accum);
|
|
||||||
}
|
|
||||||
Constraint::And(inner) => {
|
|
||||||
for c in inner {
|
|
||||||
validate_help(c, declared, accum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Constraint::Present(typ, constr) => {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
match constr {
|
|
||||||
PresenceConstraint::IncludesTag(_, tys, _, _) => {
|
|
||||||
for ty in tys {
|
|
||||||
subtract(declared, &ty.variables_detail(), accum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
PresenceConstraint::IsOpen => {}
|
|
||||||
PresenceConstraint::Pattern(_, _, expected) => {
|
|
||||||
subtract(declared, &typ.variables_detail(), accum);
|
|
||||||
subtract(declared, &expected.get_type_ref().variables_detail(), accum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -864,6 +864,32 @@ fn pattern_to_vars_by_symbol(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn single_can_def(
|
||||||
|
loc_can_pattern: Loc<Pattern>,
|
||||||
|
loc_can_expr: Loc<Expr>,
|
||||||
|
expr_var: Variable,
|
||||||
|
opt_loc_annotation: Option<Loc<crate::annotation::Annotation>>,
|
||||||
|
pattern_vars: SendMap<Symbol, Variable>,
|
||||||
|
) -> Def {
|
||||||
|
let def_annotation = opt_loc_annotation.map(|loc_annotation| Annotation {
|
||||||
|
signature: loc_annotation.value.typ,
|
||||||
|
introduced_variables: loc_annotation.value.introduced_variables,
|
||||||
|
aliases: loc_annotation.value.aliases,
|
||||||
|
region: loc_annotation.region,
|
||||||
|
});
|
||||||
|
|
||||||
|
Def {
|
||||||
|
expr_var,
|
||||||
|
loc_pattern: loc_can_pattern,
|
||||||
|
loc_expr: Loc {
|
||||||
|
region: loc_can_expr.region,
|
||||||
|
value: loc_can_expr.value,
|
||||||
|
},
|
||||||
|
pattern_vars,
|
||||||
|
annotation: def_annotation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO trim down these arguments!
|
// TODO trim down these arguments!
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
|
@ -887,25 +913,25 @@ fn canonicalize_pending_def<'a>(
|
||||||
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
AnnotationOnly(_, loc_can_pattern, loc_ann) => {
|
||||||
// annotation sans body cannot introduce new rigids that are visible in other annotations
|
// annotation sans body cannot introduce new rigids that are visible in other annotations
|
||||||
// but the rigids can show up in type error messages, so still register them
|
// but the rigids can show up in type error messages, so still register them
|
||||||
let ann =
|
let type_annotation =
|
||||||
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
||||||
|
|
||||||
// Record all the annotation's references in output.references.lookups
|
// Record all the annotation's references in output.references.lookups
|
||||||
|
|
||||||
for symbol in ann.references {
|
for symbol in type_annotation.references.iter() {
|
||||||
output.references.lookups.insert(symbol);
|
output.references.lookups.insert(*symbol);
|
||||||
output.references.referenced_type_defs.insert(symbol);
|
output.references.referenced_type_defs.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
aliases.extend(ann.aliases.clone());
|
aliases.extend(type_annotation.aliases.clone());
|
||||||
|
|
||||||
output.introduced_variables.union(&ann.introduced_variables);
|
output
|
||||||
|
.introduced_variables
|
||||||
|
.union(&type_annotation.introduced_variables);
|
||||||
|
|
||||||
pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var);
|
pattern_to_vars_by_symbol(&mut vars_by_symbol, &loc_can_pattern.value, expr_var);
|
||||||
|
|
||||||
let typ = ann.typ;
|
let arity = type_annotation.typ.arity();
|
||||||
|
|
||||||
let arity = typ.arity();
|
|
||||||
|
|
||||||
let problem = match &loc_can_pattern.value {
|
let problem = match &loc_can_pattern.value {
|
||||||
Pattern::Identifier(symbol) => RuntimeError::NoImplementationNamed {
|
Pattern::Identifier(symbol) => RuntimeError::NoImplementationNamed {
|
||||||
|
@ -963,6 +989,16 @@ fn canonicalize_pending_def<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Pattern::Identifier(symbol) = loc_can_pattern.value {
|
||||||
|
let def = single_can_def(
|
||||||
|
loc_can_pattern,
|
||||||
|
loc_can_expr,
|
||||||
|
expr_var,
|
||||||
|
Some(Loc::at(loc_ann.region, type_annotation)),
|
||||||
|
vars_by_symbol.clone(),
|
||||||
|
);
|
||||||
|
can_defs_by_symbol.insert(symbol, def);
|
||||||
|
} else {
|
||||||
for (_, (symbol, _)) in scope.idents() {
|
for (_, (symbol, _)) in scope.idents() {
|
||||||
if !vars_by_symbol.contains_key(symbol) {
|
if !vars_by_symbol.contains_key(symbol) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -983,38 +1019,39 @@ fn canonicalize_pending_def<'a>(
|
||||||
},
|
},
|
||||||
pattern_vars: vars_by_symbol.clone(),
|
pattern_vars: vars_by_symbol.clone(),
|
||||||
annotation: Some(Annotation {
|
annotation: Some(Annotation {
|
||||||
signature: typ.clone(),
|
signature: type_annotation.typ.clone(),
|
||||||
introduced_variables: output.introduced_variables.clone(),
|
introduced_variables: output.introduced_variables.clone(),
|
||||||
aliases: ann.aliases.clone(),
|
aliases: type_annotation.aliases.clone(),
|
||||||
region: loc_ann.region,
|
region: loc_ann.region,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Alias { .. } => unreachable!("Aliases are handled in a separate pass"),
|
Alias { .. } => unreachable!("Aliases are handled in a separate pass"),
|
||||||
|
|
||||||
InvalidAlias { .. } => {
|
InvalidAlias { .. } => {
|
||||||
// invalid aliases and opaques (shadowed, incorrect patterns) get ignored
|
// invalid aliases and opaques (shadowed, incorrect patterns) get ignored
|
||||||
}
|
}
|
||||||
TypedBody(loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
|
TypedBody(_loc_pattern, loc_can_pattern, loc_ann, loc_expr) => {
|
||||||
let ann =
|
let type_annotation =
|
||||||
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
canonicalize_annotation(env, scope, &loc_ann.value, loc_ann.region, var_store);
|
||||||
|
|
||||||
// Record all the annotation's references in output.references.lookups
|
// Record all the annotation's references in output.references.lookups
|
||||||
for symbol in ann.references {
|
for symbol in type_annotation.references.iter() {
|
||||||
output.references.lookups.insert(symbol);
|
output.references.lookups.insert(*symbol);
|
||||||
output.references.referenced_type_defs.insert(symbol);
|
output.references.referenced_type_defs.insert(*symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
let typ = ann.typ;
|
for (symbol, alias) in type_annotation.aliases.clone() {
|
||||||
|
|
||||||
for (symbol, alias) in ann.aliases.clone() {
|
|
||||||
aliases.insert(symbol, alias);
|
aliases.insert(symbol, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
output.introduced_variables.union(&ann.introduced_variables);
|
output
|
||||||
|
.introduced_variables
|
||||||
|
.union(&type_annotation.introduced_variables);
|
||||||
|
|
||||||
// bookkeeping for tail-call detection. If we're assigning to an
|
// bookkeeping for tail-call detection. If we're assigning to an
|
||||||
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
|
// identifier (e.g. `f = \x -> ...`), then this symbol can be tail-called.
|
||||||
|
@ -1041,42 +1078,31 @@ fn canonicalize_pending_def<'a>(
|
||||||
// reset the tailcallable_symbol
|
// reset the tailcallable_symbol
|
||||||
env.tailcallable_symbol = outer_identifier;
|
env.tailcallable_symbol = outer_identifier;
|
||||||
|
|
||||||
// see below: a closure needs a fresh References!
|
|
||||||
let mut is_closure = false;
|
|
||||||
|
|
||||||
// First, make sure we are actually assigning an identifier instead of (for example) a tag.
|
// First, make sure we are actually assigning an identifier instead of (for example) a tag.
|
||||||
//
|
//
|
||||||
// If we're assigning (UserId userId) = ... then this is certainly not a closure declaration,
|
// If we're assigning (UserId userId) = ... then this is certainly not a closure declaration,
|
||||||
// which also implies it's not a self tail call!
|
// which also implies it's not a self tail call!
|
||||||
//
|
//
|
||||||
// Only defs of the form (foo = ...) can be closure declarations or self tail calls.
|
// Only defs of the form (foo = ...) can be closure declarations or self tail calls.
|
||||||
if let (
|
if let Pattern::Identifier(symbol) = loc_can_pattern.value {
|
||||||
&ast::Pattern::Identifier(_name),
|
if let Closure(ClosureData {
|
||||||
&Pattern::Identifier(ref defined_symbol),
|
|
||||||
&Closure(ClosureData {
|
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
return_type,
|
return_type,
|
||||||
name: ref symbol,
|
name: ref closure_name,
|
||||||
ref arguments,
|
ref arguments,
|
||||||
loc_body: ref body,
|
loc_body: ref body,
|
||||||
ref captured_symbols,
|
ref captured_symbols,
|
||||||
..
|
..
|
||||||
}),
|
}) = loc_can_expr.value
|
||||||
) = (
|
{
|
||||||
&loc_pattern.value,
|
|
||||||
&loc_can_pattern.value,
|
|
||||||
&loc_can_expr.value,
|
|
||||||
) {
|
|
||||||
is_closure = true;
|
|
||||||
|
|
||||||
// Since everywhere in the code it'll be referred to by its defined name,
|
// Since everywhere in the code it'll be referred to by its defined name,
|
||||||
// remove its generated name from the closure map. (We'll re-insert it later.)
|
// remove its generated name from the closure map. (We'll re-insert it later.)
|
||||||
let references = env.closures.remove(symbol).unwrap_or_else(|| {
|
let references = env.closures.remove(closure_name).unwrap_or_else(|| {
|
||||||
panic!(
|
panic!(
|
||||||
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
|
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
|
||||||
symbol, env.closures
|
closure_name, env.closures
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1084,52 +1110,59 @@ fn canonicalize_pending_def<'a>(
|
||||||
// closures don't have a name, and therefore pick a fresh symbol. But in this
|
// closures don't have a name, and therefore pick a fresh symbol. But in this
|
||||||
// case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...`
|
// case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...`
|
||||||
// and we want to reference it by that name.
|
// and we want to reference it by that name.
|
||||||
env.closures.insert(*defined_symbol, references);
|
env.closures.insert(symbol, references);
|
||||||
|
|
||||||
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
||||||
let is_recursive = match can_output.tail_call {
|
let is_recursive = match can_output.tail_call {
|
||||||
Some(ref symbol) if symbol == defined_symbol => Recursive::TailRecursive,
|
Some(tail_symbol) if tail_symbol == symbol => Recursive::TailRecursive,
|
||||||
_ => Recursive::NotRecursive,
|
_ => Recursive::NotRecursive,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Recursion doesn't count as referencing. (If it did, all recursive functions
|
// Recursion doesn't count as referencing. (If it did, all recursive functions
|
||||||
// would result in circular def errors!)
|
// would result in circular def errors!)
|
||||||
refs_by_symbol
|
refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
|
||||||
.entry(*defined_symbol)
|
refs.lookups = refs.lookups.without(&symbol);
|
||||||
.and_modify(|(_, refs)| {
|
|
||||||
refs.lookups = refs.lookups.without(defined_symbol);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// renamed_closure_def = Some(&defined_symbol);
|
// renamed_closure_def = Some(&symbol);
|
||||||
loc_can_expr.value = Closure(ClosureData {
|
loc_can_expr.value = Closure(ClosureData {
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
return_type,
|
return_type,
|
||||||
name: *defined_symbol,
|
name: symbol,
|
||||||
captured_symbols: captured_symbols.clone(),
|
captured_symbols: captured_symbols.clone(),
|
||||||
recursive: is_recursive,
|
recursive: is_recursive,
|
||||||
arguments: arguments.clone(),
|
arguments: arguments.clone(),
|
||||||
loc_body: body.clone(),
|
loc_body: body.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Functions' references don't count in defs.
|
||||||
|
// See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its
|
||||||
|
// parent commit for the bug this fixed!
|
||||||
|
let refs = References::new();
|
||||||
|
|
||||||
|
refs_by_symbol.insert(symbol, (loc_can_pattern.region, refs));
|
||||||
|
} else {
|
||||||
|
let refs = can_output.references;
|
||||||
|
refs_by_symbol.insert(symbol, (loc_ann.region, refs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
let def = single_can_def(
|
||||||
// which defined names reference each other.
|
loc_can_pattern,
|
||||||
|
loc_can_expr,
|
||||||
|
expr_var,
|
||||||
|
Some(Loc::at(loc_ann.region, type_annotation)),
|
||||||
|
vars_by_symbol.clone(),
|
||||||
|
);
|
||||||
|
can_defs_by_symbol.insert(symbol, def);
|
||||||
|
} else {
|
||||||
for (_, (symbol, region)) in scope.idents() {
|
for (_, (symbol, region)) in scope.idents() {
|
||||||
if !vars_by_symbol.contains_key(symbol) {
|
if !vars_by_symbol.contains_key(symbol) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let refs =
|
let refs = can_output.references.clone();
|
||||||
// Functions' references don't count in defs.
|
|
||||||
// See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its
|
|
||||||
// parent commit for the bug this fixed!
|
|
||||||
if is_closure {
|
|
||||||
References::new()
|
|
||||||
} else {
|
|
||||||
can_output.references.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
refs_by_symbol.insert(*symbol, (*region, refs));
|
refs_by_symbol.insert(*symbol, (*region, refs));
|
||||||
|
|
||||||
|
@ -1146,15 +1179,16 @@ fn canonicalize_pending_def<'a>(
|
||||||
},
|
},
|
||||||
pattern_vars: vars_by_symbol.clone(),
|
pattern_vars: vars_by_symbol.clone(),
|
||||||
annotation: Some(Annotation {
|
annotation: Some(Annotation {
|
||||||
signature: typ.clone(),
|
signature: type_annotation.typ.clone(),
|
||||||
introduced_variables: output.introduced_variables.clone(),
|
introduced_variables: type_annotation.introduced_variables.clone(),
|
||||||
aliases: ann.aliases.clone(),
|
aliases: type_annotation.aliases.clone(),
|
||||||
region: loc_ann.region,
|
region: loc_ann.region,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// If we have a pattern, then the def has a body (that is, it's not a
|
// If we have a pattern, then the def has a body (that is, it's not a
|
||||||
// standalone annotation), so we need to canonicalize the pattern and expr.
|
// standalone annotation), so we need to canonicalize the pattern and expr.
|
||||||
Body(loc_pattern, loc_can_pattern, loc_expr) => {
|
Body(loc_pattern, loc_can_pattern, loc_expr) => {
|
||||||
|
@ -1184,42 +1218,31 @@ fn canonicalize_pending_def<'a>(
|
||||||
// reset the tailcallable_symbol
|
// reset the tailcallable_symbol
|
||||||
env.tailcallable_symbol = outer_identifier;
|
env.tailcallable_symbol = outer_identifier;
|
||||||
|
|
||||||
// see below: a closure needs a fresh References!
|
|
||||||
let mut is_closure = false;
|
|
||||||
|
|
||||||
// First, make sure we are actually assigning an identifier instead of (for example) a tag.
|
// First, make sure we are actually assigning an identifier instead of (for example) a tag.
|
||||||
//
|
//
|
||||||
// If we're assigning (UserId userId) = ... then this is certainly not a closure declaration,
|
// If we're assigning (UserId userId) = ... then this is certainly not a closure declaration,
|
||||||
// which also implies it's not a self tail call!
|
// which also implies it's not a self tail call!
|
||||||
//
|
//
|
||||||
// Only defs of the form (foo = ...) can be closure declarations or self tail calls.
|
// Only defs of the form (foo = ...) can be closure declarations or self tail calls.
|
||||||
if let (
|
if let Pattern::Identifier(symbol) = loc_can_pattern.value {
|
||||||
&ast::Pattern::Identifier(_name),
|
if let Closure(ClosureData {
|
||||||
&Pattern::Identifier(ref defined_symbol),
|
|
||||||
&Closure(ClosureData {
|
|
||||||
function_type,
|
function_type,
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
return_type,
|
return_type,
|
||||||
name: ref symbol,
|
name: ref closure_name,
|
||||||
ref arguments,
|
ref arguments,
|
||||||
loc_body: ref body,
|
loc_body: ref body,
|
||||||
ref captured_symbols,
|
ref captured_symbols,
|
||||||
..
|
..
|
||||||
}),
|
}) = loc_can_expr.value
|
||||||
) = (
|
{
|
||||||
&loc_pattern.value,
|
|
||||||
&loc_can_pattern.value,
|
|
||||||
&loc_can_expr.value,
|
|
||||||
) {
|
|
||||||
is_closure = true;
|
|
||||||
|
|
||||||
// Since everywhere in the code it'll be referred to by its defined name,
|
// Since everywhere in the code it'll be referred to by its defined name,
|
||||||
// remove its generated name from the closure map. (We'll re-insert it later.)
|
// remove its generated name from the closure map. (We'll re-insert it later.)
|
||||||
let references = env.closures.remove(symbol).unwrap_or_else(|| {
|
let references = env.closures.remove(closure_name).unwrap_or_else(|| {
|
||||||
panic!(
|
panic!(
|
||||||
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
|
"Tried to remove symbol {:?} from procedures, but it was not found: {:?}",
|
||||||
symbol, env.closures
|
closure_name, env.closures
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1227,20 +1250,18 @@ fn canonicalize_pending_def<'a>(
|
||||||
// closures don't have a name, and therefore pick a fresh symbol. But in this
|
// closures don't have a name, and therefore pick a fresh symbol. But in this
|
||||||
// case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...`
|
// case, the closure has a proper name (e.g. `foo` in `foo = \x y -> ...`
|
||||||
// and we want to reference it by that name.
|
// and we want to reference it by that name.
|
||||||
env.closures.insert(*defined_symbol, references);
|
env.closures.insert(symbol, references);
|
||||||
|
|
||||||
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
// The closure is self tail recursive iff it tail calls itself (by defined name).
|
||||||
let is_recursive = match can_output.tail_call {
|
let is_recursive = match can_output.tail_call {
|
||||||
Some(ref symbol) if symbol == defined_symbol => Recursive::TailRecursive,
|
Some(tail_symbol) if tail_symbol == symbol => Recursive::TailRecursive,
|
||||||
_ => Recursive::NotRecursive,
|
_ => Recursive::NotRecursive,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Recursion doesn't count as referencing. (If it did, all recursive functions
|
// Recursion doesn't count as referencing. (If it did, all recursive functions
|
||||||
// would result in circular def errors!)
|
// would result in circular def errors!)
|
||||||
refs_by_symbol
|
refs_by_symbol.entry(symbol).and_modify(|(_, refs)| {
|
||||||
.entry(*defined_symbol)
|
refs.lookups = refs.lookups.without(&symbol);
|
||||||
.and_modify(|(_, refs)| {
|
|
||||||
refs.lookups = refs.lookups.without(defined_symbol);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
loc_can_expr.value = Closure(ClosureData {
|
loc_can_expr.value = Closure(ClosureData {
|
||||||
|
@ -1248,27 +1269,36 @@ fn canonicalize_pending_def<'a>(
|
||||||
closure_type,
|
closure_type,
|
||||||
closure_ext_var,
|
closure_ext_var,
|
||||||
return_type,
|
return_type,
|
||||||
name: *defined_symbol,
|
name: symbol,
|
||||||
captured_symbols: captured_symbols.clone(),
|
captured_symbols: captured_symbols.clone(),
|
||||||
recursive: is_recursive,
|
recursive: is_recursive,
|
||||||
arguments: arguments.clone(),
|
arguments: arguments.clone(),
|
||||||
loc_body: body.clone(),
|
loc_body: body.clone(),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
|
||||||
// which defined names reference each other.
|
|
||||||
for (symbol, region) in bindings_from_patterns(std::iter::once(&loc_can_pattern)) {
|
|
||||||
let refs =
|
|
||||||
// Functions' references don't count in defs.
|
// Functions' references don't count in defs.
|
||||||
// See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its
|
// See 3d5a2560057d7f25813112dfa5309956c0f9e6a9 and its
|
||||||
// parent commit for the bug this fixed!
|
// parent commit for the bug this fixed!
|
||||||
if is_closure {
|
let refs = References::new();
|
||||||
References::new()
|
refs_by_symbol.insert(symbol, (loc_pattern.region, refs));
|
||||||
} else {
|
} else {
|
||||||
can_output.references.clone()
|
let refs = can_output.references.clone();
|
||||||
};
|
refs_by_symbol.insert(symbol, (loc_pattern.region, refs));
|
||||||
|
}
|
||||||
|
|
||||||
|
let def = single_can_def(
|
||||||
|
loc_can_pattern,
|
||||||
|
loc_can_expr,
|
||||||
|
expr_var,
|
||||||
|
None,
|
||||||
|
vars_by_symbol.clone(),
|
||||||
|
);
|
||||||
|
can_defs_by_symbol.insert(symbol, def);
|
||||||
|
} else {
|
||||||
|
// Store the referenced locals in the refs_by_symbol map, so we can later figure out
|
||||||
|
// which defined names reference each other.
|
||||||
|
for (symbol, region) in bindings_from_patterns(std::iter::once(&loc_can_pattern)) {
|
||||||
|
let refs = can_output.references.clone();
|
||||||
refs_by_symbol.insert(symbol, (region, refs));
|
refs_by_symbol.insert(symbol, (region, refs));
|
||||||
|
|
||||||
can_defs_by_symbol.insert(
|
can_defs_by_symbol.insert(
|
||||||
|
@ -1287,6 +1317,7 @@ fn canonicalize_pending_def<'a>(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
output.union(can_output);
|
output.union(can_output);
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,17 +138,7 @@ pub enum Expr {
|
||||||
field: Lowercase,
|
field: Lowercase,
|
||||||
},
|
},
|
||||||
/// field accessor as a function, e.g. (.foo) expr
|
/// field accessor as a function, e.g. (.foo) expr
|
||||||
Accessor {
|
Accessor(AccessorData),
|
||||||
/// accessors are desugared to closures; they need to have a name
|
|
||||||
/// so the closure can have a correct lambda set
|
|
||||||
name: Symbol,
|
|
||||||
function_var: Variable,
|
|
||||||
record_var: Variable,
|
|
||||||
closure_ext_var: Variable,
|
|
||||||
ext_var: Variable,
|
|
||||||
field_var: Variable,
|
|
||||||
field: Lowercase,
|
|
||||||
},
|
|
||||||
|
|
||||||
Update {
|
Update {
|
||||||
record_var: Variable,
|
record_var: Variable,
|
||||||
|
@ -217,6 +207,70 @@ pub struct ClosureData {
|
||||||
pub loc_body: Box<Loc<Expr>>,
|
pub loc_body: Box<Loc<Expr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A record accessor like `.foo`, which is equivalent to `\r -> r.foo`
|
||||||
|
/// Accessors are desugared to closures; they need to have a name
|
||||||
|
/// so the closure can have a correct lambda set.
|
||||||
|
///
|
||||||
|
/// We distinguish them from closures so we can have better error messages
|
||||||
|
/// during constraint generation.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct AccessorData {
|
||||||
|
pub name: Symbol,
|
||||||
|
pub function_var: Variable,
|
||||||
|
pub record_var: Variable,
|
||||||
|
pub closure_var: Variable,
|
||||||
|
pub closure_ext_var: Variable,
|
||||||
|
pub ext_var: Variable,
|
||||||
|
pub field_var: Variable,
|
||||||
|
pub field: Lowercase,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccessorData {
|
||||||
|
pub fn to_closure_data(self, record_symbol: Symbol) -> ClosureData {
|
||||||
|
let AccessorData {
|
||||||
|
name,
|
||||||
|
function_var,
|
||||||
|
record_var,
|
||||||
|
closure_var,
|
||||||
|
closure_ext_var,
|
||||||
|
ext_var,
|
||||||
|
field_var,
|
||||||
|
field,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
// IDEA: convert accessor from
|
||||||
|
//
|
||||||
|
// .foo
|
||||||
|
//
|
||||||
|
// into
|
||||||
|
//
|
||||||
|
// (\r -> r.foo)
|
||||||
|
let body = Expr::Access {
|
||||||
|
record_var,
|
||||||
|
ext_var,
|
||||||
|
field_var,
|
||||||
|
loc_expr: Box::new(Loc::at_zero(Expr::Var(record_symbol))),
|
||||||
|
field,
|
||||||
|
};
|
||||||
|
|
||||||
|
let loc_body = Loc::at_zero(body);
|
||||||
|
|
||||||
|
let arguments = vec![(record_var, Loc::at_zero(Pattern::Identifier(record_symbol)))];
|
||||||
|
|
||||||
|
ClosureData {
|
||||||
|
function_type: function_var,
|
||||||
|
closure_type: closure_var,
|
||||||
|
closure_ext_var,
|
||||||
|
return_type: field_var,
|
||||||
|
name,
|
||||||
|
captured_symbols: vec![],
|
||||||
|
recursive: Recursive::NotRecursive,
|
||||||
|
arguments,
|
||||||
|
loc_body: Box::new(loc_body),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Field {
|
pub struct Field {
|
||||||
pub var: Variable,
|
pub var: Variable,
|
||||||
|
@ -735,15 +789,16 @@ pub fn canonicalize_expr<'a>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::Expr::AccessorFunction(field) => (
|
ast::Expr::AccessorFunction(field) => (
|
||||||
Accessor {
|
Accessor(AccessorData {
|
||||||
name: env.gen_unique_symbol(),
|
name: env.gen_unique_symbol(),
|
||||||
function_var: var_store.fresh(),
|
function_var: var_store.fresh(),
|
||||||
record_var: var_store.fresh(),
|
record_var: var_store.fresh(),
|
||||||
ext_var: var_store.fresh(),
|
ext_var: var_store.fresh(),
|
||||||
|
closure_var: var_store.fresh(),
|
||||||
closure_ext_var: var_store.fresh(),
|
closure_ext_var: var_store.fresh(),
|
||||||
field_var: var_store.fresh(),
|
field_var: var_store.fresh(),
|
||||||
field: (*field).into(),
|
field: (*field).into(),
|
||||||
},
|
}),
|
||||||
Output::default(),
|
Output::default(),
|
||||||
),
|
),
|
||||||
ast::Expr::GlobalTag(tag) => {
|
ast::Expr::GlobalTag(tag) => {
|
||||||
|
|
|
@ -161,13 +161,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct Index(usize);
|
pub struct HumanIndex(usize);
|
||||||
|
|
||||||
impl Index {
|
impl HumanIndex {
|
||||||
pub const FIRST: Self = Index(0);
|
pub const FIRST: Self = HumanIndex(0);
|
||||||
|
|
||||||
pub fn zero_based(i: usize) -> Self {
|
pub fn zero_based(i: usize) -> Self {
|
||||||
Index(i)
|
HumanIndex(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_zero_based(self) -> usize {
|
pub fn to_zero_based(self) -> usize {
|
||||||
|
@ -175,7 +175,7 @@ impl Index {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn one_based(i: usize) -> Self {
|
pub fn one_based(i: usize) -> Self {
|
||||||
Index(i - 1)
|
HumanIndex(i - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ordinal(self) -> std::string::String {
|
pub fn ordinal(self) -> std::string::String {
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
#![allow(clippy::large_enum_variant)]
|
#![allow(clippy::large_enum_variant)]
|
||||||
|
|
||||||
pub mod all;
|
pub mod all;
|
||||||
|
pub mod soa;
|
||||||
|
|
119
compiler/collections/src/soa.rs
Normal file
119
compiler/collections/src/soa.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
use std::usize;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub struct Index<T> {
|
||||||
|
index: u32,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for Index<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
index: self.index,
|
||||||
|
_marker: self._marker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Copy for Index<T> {}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Index<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Index({})", self.index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<T> {
|
||||||
|
pub const fn new(index: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
index,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn index(&self) -> usize {
|
||||||
|
self.index as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_new(vector: &mut Vec<T>, value: T) -> Index<T> {
|
||||||
|
let index = Self::new(vector.len() as _);
|
||||||
|
|
||||||
|
vector.push(value);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
pub struct Slice<T> {
|
||||||
|
start: u32,
|
||||||
|
length: u16,
|
||||||
|
_marker: std::marker::PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for Slice<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
start: self.start,
|
||||||
|
length: self.length,
|
||||||
|
_marker: self._marker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Copy for Slice<T> {}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Slice<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Slice(start = {}, length = {})", self.start, self.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Slice<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
start: Default::default(),
|
||||||
|
length: Default::default(),
|
||||||
|
_marker: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Slice<T> {
|
||||||
|
pub const fn new(start: u32, length: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
start,
|
||||||
|
length,
|
||||||
|
_marker: std::marker::PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extend_new<I>(vector: &mut Vec<T>, values: I) -> Slice<T>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
{
|
||||||
|
let start = vector.len() as u32;
|
||||||
|
|
||||||
|
vector.extend(values);
|
||||||
|
|
||||||
|
let end = vector.len() as u32;
|
||||||
|
|
||||||
|
Self::new(start, (end - start) as u16)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn len(&self) -> usize {
|
||||||
|
self.length as _
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn is_empty(&self) -> bool {
|
||||||
|
self.length == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn indices(&self) -> std::ops::Range<usize> {
|
||||||
|
self.start as usize..(self.start as usize + self.length as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_iter(&self) -> impl Iterator<Item = Index<T>> {
|
||||||
|
self.indices().map(|i| Index::new(i as _))
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,3 +14,4 @@ roc_parse = { path = "../parse" }
|
||||||
roc_types = { path = "../types" }
|
roc_types = { path = "../types" }
|
||||||
roc_can = { path = "../can" }
|
roc_can = { path = "../can" }
|
||||||
roc_builtins = { path = "../builtins" }
|
roc_builtins = { path = "../builtins" }
|
||||||
|
arrayvec = "0.7.2"
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use roc_can::constraint::Constraint::{self, *};
|
use arrayvec::ArrayVec;
|
||||||
use roc_can::constraint::LetConstraint;
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::expected::Expected::{self, *};
|
use roc_can::expected::Expected::{self, *};
|
||||||
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntWidth, NumericBound, SignDemand};
|
||||||
use roc_collections::all::SendMap;
|
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
|
@ -12,8 +11,10 @@ use roc_types::types::Type::{self, *};
|
||||||
use roc_types::types::{AliasKind, Category};
|
use roc_types::types::{AliasKind, Category};
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[inline(always)]
|
||||||
pub fn add_numeric_bound_constr(
|
pub fn add_numeric_bound_constr(
|
||||||
constrs: &mut Vec<Constraint>,
|
constraints: &mut Constraints,
|
||||||
|
num_constraints: &mut impl Extend<Constraint>,
|
||||||
num_type: Type,
|
num_type: Type,
|
||||||
bound: impl TypedNumericBound,
|
bound: impl TypedNumericBound,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
@ -27,12 +28,12 @@ pub fn add_numeric_bound_constr(
|
||||||
0 => total_num_type,
|
0 => total_num_type,
|
||||||
1 => {
|
1 => {
|
||||||
let actual_type = Variable(range[0]);
|
let actual_type = Variable(range[0]);
|
||||||
constrs.push(Eq(
|
let expected = Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region);
|
||||||
total_num_type.clone(),
|
let because_suffix =
|
||||||
Expected::ForReason(Reason::NumericLiteralSuffix, actual_type, region),
|
constraints.equal_types(total_num_type.clone(), expected, category, region);
|
||||||
category,
|
|
||||||
region,
|
num_constraints.extend([because_suffix]);
|
||||||
));
|
|
||||||
total_num_type
|
total_num_type
|
||||||
}
|
}
|
||||||
_ => RangedNumber(Box::new(total_num_type), range),
|
_ => RangedNumber(Box::new(total_num_type), range),
|
||||||
|
@ -41,6 +42,7 @@ pub fn add_numeric_bound_constr(
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn int_literal(
|
pub fn int_literal(
|
||||||
|
constraints: &mut Constraints,
|
||||||
num_var: Variable,
|
num_var: Variable,
|
||||||
precision_var: Variable,
|
precision_var: Variable,
|
||||||
expected: Expected<Type>,
|
expected: Expected<Type>,
|
||||||
|
@ -49,31 +51,35 @@ pub fn int_literal(
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let reason = Reason::IntLiteral;
|
let reason = Reason::IntLiteral;
|
||||||
|
|
||||||
let mut constrs = Vec::with_capacity(3);
|
// Always add the bound first; this improves the resolved type quality in case it's an alias like "U8".
|
||||||
// Always add the bound first; this improves the resolved type quality in case it's an alias
|
let mut constrs = ArrayVec::<_, 3>::new();
|
||||||
// like "U8".
|
|
||||||
let num_type = add_numeric_bound_constr(
|
let num_type = add_numeric_bound_constr(
|
||||||
|
constraints,
|
||||||
&mut constrs,
|
&mut constrs,
|
||||||
Variable(num_var),
|
Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Num,
|
Category::Num,
|
||||||
);
|
);
|
||||||
constrs.extend(vec![
|
|
||||||
Eq(
|
constrs.extend([
|
||||||
|
constraints.equal_types(
|
||||||
num_type.clone(),
|
num_type.clone(),
|
||||||
ForReason(reason, num_int(Type::Variable(precision_var)), region),
|
ForReason(reason, num_int(Type::Variable(precision_var)), region),
|
||||||
Category::Int,
|
Category::Int,
|
||||||
region,
|
region,
|
||||||
),
|
),
|
||||||
Eq(num_type, expected, Category::Int, region),
|
constraints.equal_types(num_type, expected, Category::Int, region),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
exists(vec![num_var], And(constrs))
|
// TODO the precision_var is not part of the exists here; for float it is. Which is correct?
|
||||||
|
let and_constraint = constraints.and_constraint(constrs);
|
||||||
|
constraints.exists([num_var], and_constraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn float_literal(
|
pub fn float_literal(
|
||||||
|
constraints: &mut Constraints,
|
||||||
num_var: Variable,
|
num_var: Variable,
|
||||||
precision_var: Variable,
|
precision_var: Variable,
|
||||||
expected: Expected<Type>,
|
expected: Expected<Type>,
|
||||||
|
@ -82,29 +88,33 @@ pub fn float_literal(
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let reason = Reason::FloatLiteral;
|
let reason = Reason::FloatLiteral;
|
||||||
|
|
||||||
let mut constrs = Vec::with_capacity(3);
|
let mut constrs = ArrayVec::<_, 3>::new();
|
||||||
let num_type = add_numeric_bound_constr(
|
let num_type = add_numeric_bound_constr(
|
||||||
|
constraints,
|
||||||
&mut constrs,
|
&mut constrs,
|
||||||
Variable(num_var),
|
Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
region,
|
region,
|
||||||
Category::Float,
|
Category::Float,
|
||||||
);
|
);
|
||||||
constrs.extend(vec![
|
|
||||||
Eq(
|
constrs.extend([
|
||||||
|
constraints.equal_types(
|
||||||
num_type.clone(),
|
num_type.clone(),
|
||||||
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
ForReason(reason, num_float(Type::Variable(precision_var)), region),
|
||||||
Category::Float,
|
Category::Float,
|
||||||
region,
|
region,
|
||||||
),
|
),
|
||||||
Eq(num_type, expected, Category::Float, region),
|
constraints.equal_types(num_type, expected, Category::Float, region),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
exists(vec![num_var, precision_var], And(constrs))
|
let and_constraint = constraints.and_constraint(constrs);
|
||||||
|
constraints.exists([num_var, precision_var], and_constraint)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn num_literal(
|
pub fn num_literal(
|
||||||
|
constraints: &mut Constraints,
|
||||||
num_var: Variable,
|
num_var: Variable,
|
||||||
expected: Expected<Type>,
|
expected: Expected<Type>,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
@ -112,23 +122,20 @@ pub fn num_literal(
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
let open_number_type = crate::builtins::num_num(Type::Variable(num_var));
|
||||||
|
|
||||||
let mut constrs = Vec::with_capacity(3);
|
let mut constrs = ArrayVec::<_, 2>::new();
|
||||||
let num_type =
|
let num_type = add_numeric_bound_constr(
|
||||||
add_numeric_bound_constr(&mut constrs, open_number_type, bound, region, Category::Num);
|
constraints,
|
||||||
constrs.extend(vec![Eq(num_type, expected, Category::Num, region)]);
|
&mut constrs,
|
||||||
|
open_number_type,
|
||||||
|
bound,
|
||||||
|
region,
|
||||||
|
Category::Num,
|
||||||
|
);
|
||||||
|
|
||||||
exists(vec![num_var], And(constrs))
|
constrs.extend([constraints.equal_types(num_type, expected, Category::Num, region)]);
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
let and_constraint = constraints.and_constraint(constrs);
|
||||||
pub fn exists(flex_vars: Vec<Variable>, constraint: Constraint) -> Constraint {
|
constraints.exists([num_var], and_constraint)
|
||||||
Let(Box::new(LetConstraint {
|
|
||||||
rigid_vars: Vec::new(),
|
|
||||||
flex_vars,
|
|
||||||
def_types: SendMap::default(),
|
|
||||||
defs_constraint: constraint,
|
|
||||||
ret_constraint: Constraint::True,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,5 @@
|
||||||
use crate::expr::constrain_decls;
|
|
||||||
use roc_builtins::std::StdLib;
|
use roc_builtins::std::StdLib;
|
||||||
use roc_can::constraint::{Constraint, LetConstraint};
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::def::Declaration;
|
use roc_can::def::Declaration;
|
||||||
use roc_collections::all::{MutMap, MutSet, SendMap};
|
use roc_collections::all::{MutMap, MutSet, SendMap};
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::{ModuleId, Symbol};
|
||||||
|
@ -17,13 +16,12 @@ pub enum ExposedModuleTypes {
|
||||||
Valid(MutMap<Symbol, SolvedType>, MutMap<Symbol, Alias>),
|
Valid(MutMap<Symbol, SolvedType>, MutMap<Symbol, Alias>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConstrainedModule {
|
pub fn constrain_module(
|
||||||
pub unused_imports: MutMap<ModuleId, Region>,
|
constraints: &mut Constraints,
|
||||||
pub constraint: Constraint,
|
declarations: &[Declaration],
|
||||||
}
|
home: ModuleId,
|
||||||
|
) -> Constraint {
|
||||||
pub fn constrain_module(declarations: &[Declaration], home: ModuleId) -> Constraint {
|
crate::expr::constrain_decls(constraints, home, declarations)
|
||||||
constrain_decls(home, declarations)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -33,11 +31,11 @@ pub struct Import {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn constrain_imported_values(
|
pub fn constrain_imported_values(
|
||||||
|
constraints: &mut Constraints,
|
||||||
imports: Vec<Import>,
|
imports: Vec<Import>,
|
||||||
body_con: Constraint,
|
body_con: Constraint,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> (Vec<Variable>, Constraint) {
|
) -> (Vec<Variable>, Constraint) {
|
||||||
use Constraint::*;
|
|
||||||
let mut def_types = SendMap::default();
|
let mut def_types = SendMap::default();
|
||||||
let mut rigid_vars = Vec::new();
|
let mut rigid_vars = Vec::new();
|
||||||
|
|
||||||
|
@ -84,24 +82,19 @@ pub fn constrain_imported_values(
|
||||||
|
|
||||||
(
|
(
|
||||||
rigid_vars.clone(),
|
rigid_vars.clone(),
|
||||||
Let(Box::new(LetConstraint {
|
constraints.let_constraint(rigid_vars, [], def_types, Constraint::True, body_con),
|
||||||
rigid_vars,
|
|
||||||
flex_vars: Vec::new(),
|
|
||||||
def_types,
|
|
||||||
defs_constraint: True,
|
|
||||||
ret_constraint: body_con,
|
|
||||||
})),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run pre_constrain_imports to get imported_symbols and imported_aliases.
|
/// Run pre_constrain_imports to get imported_symbols and imported_aliases.
|
||||||
pub fn constrain_imports(
|
pub fn constrain_imports(
|
||||||
|
constraints: &mut Constraints,
|
||||||
imported_symbols: Vec<Import>,
|
imported_symbols: Vec<Import>,
|
||||||
constraint: Constraint,
|
constraint: Constraint,
|
||||||
var_store: &mut VarStore,
|
var_store: &mut VarStore,
|
||||||
) -> Constraint {
|
) -> Constraint {
|
||||||
let (_introduced_rigids, constraint) =
|
let (_introduced_rigids, constraint) =
|
||||||
constrain_imported_values(imported_symbols, constraint, var_store);
|
constrain_imported_values(constraints, imported_symbols, constraint, var_store);
|
||||||
|
|
||||||
// TODO determine what to do with those rigids
|
// TODO determine what to do with those rigids
|
||||||
// for var in introduced_rigids {
|
// for var in introduced_rigids {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::builtins;
|
use crate::builtins;
|
||||||
use crate::expr::{constrain_expr, Env};
|
use crate::expr::{constrain_expr, Env};
|
||||||
use roc_can::constraint::{Constraint, PresenceConstraint};
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_can::pattern::Pattern::{self, *};
|
use roc_can::pattern::Pattern::{self, *};
|
||||||
use roc_can::pattern::{DestructType, RecordDestruct};
|
use roc_can::pattern::{DestructType, RecordDestruct};
|
||||||
use roc_collections::all::{Index, SendMap};
|
use roc_collections::all::{HumanIndex, SendMap};
|
||||||
use roc_module::ident::Lowercase;
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
@ -27,7 +27,7 @@ pub struct PatternState {
|
||||||
/// definition has an annotation, we instead now add `x => Int`.
|
/// definition has an annotation, we instead now add `x => Int`.
|
||||||
pub fn headers_from_annotation(
|
pub fn headers_from_annotation(
|
||||||
pattern: &Pattern,
|
pattern: &Pattern,
|
||||||
annotation: &Loc<Type>,
|
annotation: &Loc<&Type>,
|
||||||
) -> Option<SendMap<Symbol, Loc<Type>>> {
|
) -> Option<SendMap<Symbol, Loc<Type>>> {
|
||||||
let mut headers = SendMap::default();
|
let mut headers = SendMap::default();
|
||||||
// Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int`
|
// Check that the annotation structurally agrees with the pattern, preventing e.g. `{ x, y } : Int`
|
||||||
|
@ -44,12 +44,13 @@ pub fn headers_from_annotation(
|
||||||
|
|
||||||
fn headers_from_annotation_help(
|
fn headers_from_annotation_help(
|
||||||
pattern: &Pattern,
|
pattern: &Pattern,
|
||||||
annotation: &Loc<Type>,
|
annotation: &Loc<&Type>,
|
||||||
headers: &mut SendMap<Symbol, Loc<Type>>,
|
headers: &mut SendMap<Symbol, Loc<Type>>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match pattern {
|
match pattern {
|
||||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||||
headers.insert(*symbol, annotation.clone());
|
let typ = Loc::at(annotation.region, annotation.value.clone());
|
||||||
|
headers.insert(*symbol, typ);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Underscore
|
Underscore
|
||||||
|
@ -106,7 +107,7 @@ fn headers_from_annotation_help(
|
||||||
.all(|(arg_pattern, arg_type)| {
|
.all(|(arg_pattern, arg_type)| {
|
||||||
headers_from_annotation_help(
|
headers_from_annotation_help(
|
||||||
&arg_pattern.1.value,
|
&arg_pattern.1.value,
|
||||||
&Loc::at(annotation.region, arg_type.clone()),
|
&Loc::at(annotation.region, arg_type),
|
||||||
headers,
|
headers,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -135,12 +136,13 @@ fn headers_from_annotation_help(
|
||||||
&& type_arguments.len() == pat_type_arguments.len()
|
&& type_arguments.len() == pat_type_arguments.len()
|
||||||
&& lambda_set_variables.len() == pat_lambda_set_variables.len() =>
|
&& lambda_set_variables.len() == pat_lambda_set_variables.len() =>
|
||||||
{
|
{
|
||||||
headers.insert(*opaque, annotation.clone());
|
let typ = Loc::at(annotation.region, annotation.value.clone());
|
||||||
|
headers.insert(*opaque, typ);
|
||||||
|
|
||||||
let (_, argument_pat) = &**argument;
|
let (_, argument_pat) = &**argument;
|
||||||
headers_from_annotation_help(
|
headers_from_annotation_help(
|
||||||
&argument_pat.value,
|
&argument_pat.value,
|
||||||
&Loc::at(annotation.region, (**actual).clone()),
|
&Loc::at(annotation.region, actual),
|
||||||
headers,
|
headers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -153,6 +155,7 @@ fn headers_from_annotation_help(
|
||||||
/// initialize the Vecs in PatternState using with_capacity
|
/// initialize the Vecs in PatternState using with_capacity
|
||||||
/// based on its knowledge of their lengths.
|
/// based on its knowledge of their lengths.
|
||||||
pub fn constrain_pattern(
|
pub fn constrain_pattern(
|
||||||
|
constraints: &mut Constraints,
|
||||||
env: &Env,
|
env: &Env,
|
||||||
pattern: &Pattern,
|
pattern: &Pattern,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
@ -167,20 +170,18 @@ pub fn constrain_pattern(
|
||||||
// A -> ""
|
// A -> ""
|
||||||
// _ -> ""
|
// _ -> ""
|
||||||
// so, we know that "x" (in this case, a tag union) must be open.
|
// so, we know that "x" (in this case, a tag union) must be open.
|
||||||
state.constraints.push(Constraint::Present(
|
state
|
||||||
expected.get_type(),
|
.constraints
|
||||||
PresenceConstraint::IsOpen,
|
.push(constraints.is_open_type(expected.get_type()));
|
||||||
));
|
|
||||||
}
|
}
|
||||||
UnsupportedPattern(_) | MalformedPattern(_, _) | OpaqueNotInScope(..) => {
|
UnsupportedPattern(_) | MalformedPattern(_, _) | OpaqueNotInScope(..) => {
|
||||||
// Erroneous patterns don't add any constraints.
|
// Erroneous patterns don't add any constraints.
|
||||||
}
|
}
|
||||||
|
|
||||||
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
Identifier(symbol) | Shadowed(_, _, symbol) => {
|
||||||
state.constraints.push(Constraint::Present(
|
state
|
||||||
expected.get_type_ref().clone(),
|
.constraints
|
||||||
PresenceConstraint::IsOpen,
|
.push(constraints.is_open_type(expected.get_type_ref().clone()));
|
||||||
));
|
|
||||||
state.headers.insert(
|
state.headers.insert(
|
||||||
*symbol,
|
*symbol,
|
||||||
Loc {
|
Loc {
|
||||||
|
@ -196,6 +197,7 @@ pub fn constrain_pattern(
|
||||||
let num_type = builtins::num_num(Type::Variable(var));
|
let num_type = builtins::num_num(Type::Variable(var));
|
||||||
|
|
||||||
let num_type = builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
|
constraints,
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
num_type,
|
num_type,
|
||||||
bound,
|
bound,
|
||||||
|
@ -203,11 +205,11 @@ pub fn constrain_pattern(
|
||||||
Category::Num,
|
Category::Num,
|
||||||
);
|
);
|
||||||
|
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(constraints.equal_pattern_types(
|
||||||
region,
|
|
||||||
PatternCategory::Num,
|
|
||||||
num_type,
|
num_type,
|
||||||
expected,
|
expected,
|
||||||
|
PatternCategory::Num,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +217,7 @@ pub fn constrain_pattern(
|
||||||
// First constraint on the free num var; this improves the resolved type quality in
|
// First constraint on the free num var; this improves the resolved type quality in
|
||||||
// case the bound is an alias.
|
// case the bound is an alias.
|
||||||
let num_type = builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
|
constraints,
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
Type::Variable(num_var),
|
Type::Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
|
@ -225,7 +228,7 @@ pub fn constrain_pattern(
|
||||||
// Link the free num var with the int var and our expectation.
|
// Link the free num var with the int var and our expectation.
|
||||||
let int_type = builtins::num_int(Type::Variable(precision_var));
|
let int_type = builtins::num_int(Type::Variable(precision_var));
|
||||||
|
|
||||||
state.constraints.push(Constraint::Eq(
|
state.constraints.push(constraints.equal_types(
|
||||||
num_type, // TODO check me if something breaks!
|
num_type, // TODO check me if something breaks!
|
||||||
Expected::NoExpectation(int_type),
|
Expected::NoExpectation(int_type),
|
||||||
Category::Int,
|
Category::Int,
|
||||||
|
@ -233,11 +236,11 @@ pub fn constrain_pattern(
|
||||||
));
|
));
|
||||||
|
|
||||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(constraints.equal_pattern_types(
|
||||||
region,
|
|
||||||
PatternCategory::Int,
|
|
||||||
Type::Variable(num_var),
|
Type::Variable(num_var),
|
||||||
expected,
|
expected,
|
||||||
|
PatternCategory::Int,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +248,7 @@ pub fn constrain_pattern(
|
||||||
// First constraint on the free num var; this improves the resolved type quality in
|
// First constraint on the free num var; this improves the resolved type quality in
|
||||||
// case the bound is an alias.
|
// case the bound is an alias.
|
||||||
let num_type = builtins::add_numeric_bound_constr(
|
let num_type = builtins::add_numeric_bound_constr(
|
||||||
|
constraints,
|
||||||
&mut state.constraints,
|
&mut state.constraints,
|
||||||
Type::Variable(num_var),
|
Type::Variable(num_var),
|
||||||
bound,
|
bound,
|
||||||
|
@ -255,7 +259,7 @@ pub fn constrain_pattern(
|
||||||
// Link the free num var with the float var and our expectation.
|
// Link the free num var with the float var and our expectation.
|
||||||
let float_type = builtins::num_float(Type::Variable(precision_var));
|
let float_type = builtins::num_float(Type::Variable(precision_var));
|
||||||
|
|
||||||
state.constraints.push(Constraint::Eq(
|
state.constraints.push(constraints.equal_types(
|
||||||
num_type.clone(), // TODO check me if something breaks!
|
num_type.clone(), // TODO check me if something breaks!
|
||||||
Expected::NoExpectation(float_type),
|
Expected::NoExpectation(float_type),
|
||||||
Category::Float,
|
Category::Float,
|
||||||
|
@ -263,29 +267,29 @@ pub fn constrain_pattern(
|
||||||
));
|
));
|
||||||
|
|
||||||
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
// Also constrain the pattern against the num var, again to reuse aliases if they're present.
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(constraints.equal_pattern_types(
|
||||||
region,
|
|
||||||
PatternCategory::Float,
|
|
||||||
num_type, // TODO check me if something breaks!
|
num_type, // TODO check me if something breaks!
|
||||||
expected,
|
expected,
|
||||||
|
PatternCategory::Float,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
StrLiteral(_) => {
|
StrLiteral(_) => {
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(constraints.equal_pattern_types(
|
||||||
region,
|
|
||||||
PatternCategory::Str,
|
|
||||||
builtins::str_type(),
|
builtins::str_type(),
|
||||||
expected,
|
expected,
|
||||||
|
PatternCategory::Str,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleQuote(_) => {
|
SingleQuote(_) => {
|
||||||
state.constraints.push(Constraint::Pattern(
|
state.constraints.push(constraints.equal_pattern_types(
|
||||||
region,
|
|
||||||
PatternCategory::Character,
|
|
||||||
builtins::num_u32(),
|
builtins::num_u32(),
|
||||||
expected,
|
expected,
|
||||||
|
PatternCategory::Character,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,36 +326,39 @@ pub fn constrain_pattern(
|
||||||
|
|
||||||
let field_type = match typ {
|
let field_type = match typ {
|
||||||
DestructType::Guard(guard_var, loc_guard) => {
|
DestructType::Guard(guard_var, loc_guard) => {
|
||||||
state.constraints.push(Constraint::Present(
|
state.constraints.push(constraints.pattern_presence(
|
||||||
Type::Variable(*guard_var),
|
Type::Variable(*guard_var),
|
||||||
PresenceConstraint::Pattern(
|
|
||||||
region,
|
|
||||||
PatternCategory::PatternGuard,
|
|
||||||
PExpected::ForReason(
|
PExpected::ForReason(
|
||||||
PReason::PatternGuard,
|
PReason::PatternGuard,
|
||||||
pat_type.clone(),
|
pat_type.clone(),
|
||||||
loc_guard.region,
|
loc_guard.region,
|
||||||
),
|
),
|
||||||
),
|
PatternCategory::PatternGuard,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
state.vars.push(*guard_var);
|
state.vars.push(*guard_var);
|
||||||
|
|
||||||
constrain_pattern(env, &loc_guard.value, loc_guard.region, expected, state);
|
constrain_pattern(
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
&loc_guard.value,
|
||||||
|
loc_guard.region,
|
||||||
|
expected,
|
||||||
|
state,
|
||||||
|
);
|
||||||
|
|
||||||
RecordField::Demanded(pat_type)
|
RecordField::Demanded(pat_type)
|
||||||
}
|
}
|
||||||
DestructType::Optional(expr_var, loc_expr) => {
|
DestructType::Optional(expr_var, loc_expr) => {
|
||||||
state.constraints.push(Constraint::Present(
|
state.constraints.push(constraints.pattern_presence(
|
||||||
Type::Variable(*expr_var),
|
Type::Variable(*expr_var),
|
||||||
PresenceConstraint::Pattern(
|
|
||||||
region,
|
|
||||||
PatternCategory::PatternDefault,
|
|
||||||
PExpected::ForReason(
|
PExpected::ForReason(
|
||||||
PReason::OptionalField,
|
PReason::OptionalField,
|
||||||
pat_type.clone(),
|
pat_type.clone(),
|
||||||
loc_expr.region,
|
loc_expr.region,
|
||||||
),
|
),
|
||||||
),
|
PatternCategory::PatternDefault,
|
||||||
|
region,
|
||||||
));
|
));
|
||||||
|
|
||||||
state.vars.push(*expr_var);
|
state.vars.push(*expr_var);
|
||||||
|
@ -362,8 +369,13 @@ pub fn constrain_pattern(
|
||||||
loc_expr.region,
|
loc_expr.region,
|
||||||
);
|
);
|
||||||
|
|
||||||
let expr_con =
|
let expr_con = constrain_expr(
|
||||||
constrain_expr(env, loc_expr.region, &loc_expr.value, expr_expected);
|
constraints,
|
||||||
|
env,
|
||||||
|
loc_expr.region,
|
||||||
|
&loc_expr.value,
|
||||||
|
expr_expected,
|
||||||
|
);
|
||||||
state.constraints.push(expr_con);
|
state.constraints.push(expr_con);
|
||||||
|
|
||||||
RecordField::Optional(pat_type)
|
RecordField::Optional(pat_type)
|
||||||
|
@ -381,16 +393,18 @@ pub fn constrain_pattern(
|
||||||
|
|
||||||
let record_type = Type::Record(field_types, Box::new(ext_type));
|
let record_type = Type::Record(field_types, Box::new(ext_type));
|
||||||
|
|
||||||
let whole_con = Constraint::Eq(
|
let whole_con = constraints.equal_types(
|
||||||
Type::Variable(*whole_var),
|
Type::Variable(*whole_var),
|
||||||
Expected::NoExpectation(record_type),
|
Expected::NoExpectation(record_type),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
let record_con = Constraint::Present(
|
let record_con = constraints.pattern_presence(
|
||||||
Type::Variable(*whole_var),
|
Type::Variable(*whole_var),
|
||||||
PresenceConstraint::Pattern(region, PatternCategory::Record, expected),
|
expected,
|
||||||
|
PatternCategory::Record,
|
||||||
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
state.constraints.push(whole_con);
|
state.constraints.push(whole_con);
|
||||||
|
@ -412,29 +426,36 @@ pub fn constrain_pattern(
|
||||||
let expected = PExpected::ForReason(
|
let expected = PExpected::ForReason(
|
||||||
PReason::TagArg {
|
PReason::TagArg {
|
||||||
tag_name: tag_name.clone(),
|
tag_name: tag_name.clone(),
|
||||||
index: Index::zero_based(index),
|
index: HumanIndex::zero_based(index),
|
||||||
},
|
},
|
||||||
pattern_type,
|
pattern_type,
|
||||||
region,
|
region,
|
||||||
);
|
);
|
||||||
constrain_pattern(env, &loc_pattern.value, loc_pattern.region, expected, state);
|
constrain_pattern(
|
||||||
|
constraints,
|
||||||
|
env,
|
||||||
|
&loc_pattern.value,
|
||||||
|
loc_pattern.region,
|
||||||
|
expected,
|
||||||
|
state,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pat_category = PatternCategory::Ctor(tag_name.clone());
|
let pat_category = PatternCategory::Ctor(tag_name.clone());
|
||||||
|
|
||||||
let whole_con = Constraint::Present(
|
let whole_con = constraints.includes_tag(
|
||||||
expected.clone().get_type(),
|
expected.clone().get_type(),
|
||||||
PresenceConstraint::IncludesTag(
|
|
||||||
tag_name.clone(),
|
tag_name.clone(),
|
||||||
argument_types.clone(),
|
argument_types.clone(),
|
||||||
region,
|
|
||||||
pat_category.clone(),
|
pat_category.clone(),
|
||||||
),
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
let tag_con = Constraint::Present(
|
let tag_con = constraints.pattern_presence(
|
||||||
Type::Variable(*whole_var),
|
Type::Variable(*whole_var),
|
||||||
PresenceConstraint::Pattern(region, pat_category, expected),
|
expected,
|
||||||
|
pat_category,
|
||||||
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
state.vars.push(*whole_var);
|
state.vars.push(*whole_var);
|
||||||
|
@ -466,6 +487,7 @@ pub fn constrain_pattern(
|
||||||
// First, add a constraint for the argument "who"
|
// First, add a constraint for the argument "who"
|
||||||
let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone());
|
let arg_pattern_expected = PExpected::NoExpectation(arg_pattern_type.clone());
|
||||||
constrain_pattern(
|
constrain_pattern(
|
||||||
|
constraints,
|
||||||
env,
|
env,
|
||||||
&loc_arg_pattern.value,
|
&loc_arg_pattern.value,
|
||||||
loc_arg_pattern.region,
|
loc_arg_pattern.region,
|
||||||
|
@ -474,7 +496,7 @@ pub fn constrain_pattern(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Next, link `whole_var` to the opaque type of "@Id who"
|
// Next, link `whole_var` to the opaque type of "@Id who"
|
||||||
let whole_con = Constraint::Eq(
|
let whole_con = constraints.equal_types(
|
||||||
Type::Variable(*whole_var),
|
Type::Variable(*whole_var),
|
||||||
Expected::NoExpectation(opaque_type),
|
Expected::NoExpectation(opaque_type),
|
||||||
Category::Storage(std::file!(), std::line!()),
|
Category::Storage(std::file!(), std::line!()),
|
||||||
|
@ -484,7 +506,7 @@ pub fn constrain_pattern(
|
||||||
// Link the entire wrapped opaque type (with the now-constrained argument) to the type
|
// Link the entire wrapped opaque type (with the now-constrained argument) to the type
|
||||||
// variables of the opaque type
|
// variables of the opaque type
|
||||||
// TODO: better expectation here
|
// TODO: better expectation here
|
||||||
let link_type_variables_con = Constraint::Eq(
|
let link_type_variables_con = constraints.equal_types(
|
||||||
(**specialized_def_type).clone(),
|
(**specialized_def_type).clone(),
|
||||||
Expected::NoExpectation(arg_pattern_type),
|
Expected::NoExpectation(arg_pattern_type),
|
||||||
Category::OpaqueWrap(*opaque),
|
Category::OpaqueWrap(*opaque),
|
||||||
|
@ -492,9 +514,11 @@ pub fn constrain_pattern(
|
||||||
);
|
);
|
||||||
|
|
||||||
// Next, link `whole_var` (the type of "@Id who") to the expected type
|
// Next, link `whole_var` (the type of "@Id who") to the expected type
|
||||||
let opaque_pattern_con = Constraint::Present(
|
let opaque_pattern_con = constraints.pattern_presence(
|
||||||
Type::Variable(*whole_var),
|
Type::Variable(*whole_var),
|
||||||
PresenceConstraint::Pattern(region, PatternCategory::Opaque(*opaque), expected),
|
expected,
|
||||||
|
PatternCategory::Opaque(*opaque),
|
||||||
|
region,
|
||||||
);
|
);
|
||||||
|
|
||||||
state
|
state
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use roc_collections::all::{Index, MutMap};
|
//! Exhaustiveness checking, based on "Warning for pattern matching" (Luc Maranget, 2007).
|
||||||
|
//! http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
|
||||||
|
|
||||||
|
use roc_collections::all::{HumanIndex, MutMap};
|
||||||
use roc_module::ident::{Lowercase, TagIdIntType, TagName};
|
use roc_module::ident::{Lowercase, TagIdIntType, TagName};
|
||||||
use roc_region::all::Region;
|
use roc_region::all::Region;
|
||||||
use roc_std::RocDec;
|
use roc_std::RocDec;
|
||||||
|
@ -70,7 +73,7 @@ pub enum Error {
|
||||||
Redundant {
|
Redundant {
|
||||||
overall_region: Region,
|
overall_region: Region,
|
||||||
branch_region: Region,
|
branch_region: Region,
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -293,6 +293,9 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
|
|
||||||
SpaceBefore(ann, spaces) => {
|
SpaceBefore(ann, spaces) => {
|
||||||
buf.newline();
|
buf.newline();
|
||||||
|
|
||||||
|
buf.indent(indent);
|
||||||
|
|
||||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
ann.format_with_options(buf, parens, Newlines::No, indent)
|
ann.format_with_options(buf, parens, Newlines::No, indent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,4 +90,116 @@ impl<'a> Buf<'a> {
|
||||||
self.spaces_to_flush = 0;
|
self.spaces_to_flush = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensures the text ends in a newline with no whitespace preceding it.
|
||||||
|
pub fn fmt_end_of_file(&mut self) {
|
||||||
|
fmt_text_eof(&mut self.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures the text ends in a newline with no whitespace preceding it.
|
||||||
|
fn fmt_text_eof(text: &mut bumpalo::collections::String<'_>) {
|
||||||
|
let mut chars_rev = text.chars().rev();
|
||||||
|
let mut last_whitespace = None;
|
||||||
|
let mut last_whitespace_index = text.len();
|
||||||
|
|
||||||
|
// Keep going until we either run out of characters or encounter one
|
||||||
|
// that isn't whitespace.
|
||||||
|
loop {
|
||||||
|
match chars_rev.next() {
|
||||||
|
Some(ch) if ch.is_whitespace() => {
|
||||||
|
last_whitespace = Some(ch);
|
||||||
|
last_whitespace_index -= 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match last_whitespace {
|
||||||
|
Some('\n') => {
|
||||||
|
// There may have been more whitespace after this newline; remove it!
|
||||||
|
text.truncate(last_whitespace_index + '\n'.len_utf8());
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
// There's some whitespace at the end of this file, but the first
|
||||||
|
// whitespace char after the last non-whitespace char isn't a newline.
|
||||||
|
// So replace that whitespace char (and everything after it) with a newline.
|
||||||
|
text.replace_range(last_whitespace_index.., "\n");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
debug_assert!(last_whitespace_index == text.len());
|
||||||
|
debug_assert!(!text.ends_with(char::is_whitespace));
|
||||||
|
|
||||||
|
// This doesn't end in whitespace at all, so add a newline.
|
||||||
|
text.push('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eof_text_ends_with_newline() {
|
||||||
|
use bumpalo::{collections::String, Bump};
|
||||||
|
|
||||||
|
let arena = Bump::new();
|
||||||
|
let input = "This should be a newline:\n";
|
||||||
|
let mut text = String::from_str_in(input, &arena);
|
||||||
|
|
||||||
|
fmt_text_eof(&mut text);
|
||||||
|
|
||||||
|
// This should be unchanged!
|
||||||
|
assert_eq!(text.as_str(), input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eof_text_ends_with_whitespace() {
|
||||||
|
use bumpalo::{collections::String, Bump};
|
||||||
|
|
||||||
|
let arena = Bump::new();
|
||||||
|
let input = "This should be a newline: \t";
|
||||||
|
let mut text = String::from_str_in(input, &arena);
|
||||||
|
|
||||||
|
fmt_text_eof(&mut text);
|
||||||
|
|
||||||
|
assert_eq!(text.as_str(), "This should be a newline:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eof_text_ends_with_whitespace_then_newline() {
|
||||||
|
use bumpalo::{collections::String, Bump};
|
||||||
|
|
||||||
|
let arena = Bump::new();
|
||||||
|
let input = "This should be a newline: \n";
|
||||||
|
let mut text = String::from_str_in(input, &arena);
|
||||||
|
|
||||||
|
fmt_text_eof(&mut text);
|
||||||
|
|
||||||
|
assert_eq!(text.as_str(), "This should be a newline:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eof_text_ends_with_no_whitespace() {
|
||||||
|
use bumpalo::{collections::String, Bump};
|
||||||
|
|
||||||
|
let arena = Bump::new();
|
||||||
|
let input = "This should be a newline:";
|
||||||
|
let mut text = String::from_str_in(input, &arena);
|
||||||
|
|
||||||
|
fmt_text_eof(&mut text);
|
||||||
|
|
||||||
|
assert_eq!(text.as_str(), "This should be a newline:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eof_text_is_empty() {
|
||||||
|
use bumpalo::{collections::String, Bump};
|
||||||
|
|
||||||
|
let arena = Bump::new();
|
||||||
|
let input = "";
|
||||||
|
let mut text = String::from_str_in(input, &arena);
|
||||||
|
|
||||||
|
fmt_text_eof(&mut text);
|
||||||
|
|
||||||
|
assert_eq!(text.as_str(), "\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2984,6 +2984,18 @@ mod test_fmt {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiline_higher_order_function() {
|
||||||
|
expr_formats_same(indoc!(
|
||||||
|
r#"
|
||||||
|
foo :
|
||||||
|
(Str -> Bool) -> Bool
|
||||||
|
|
||||||
|
42
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// Test that everything under examples/ is formatted correctly
|
/// Test that everything under examples/ is formatted correctly
|
||||||
/// If this test fails on your diff, it probably means you need to re-format the examples.
|
/// If this test fails on your diff, it probably means you need to re-format the examples.
|
||||||
|
@ -3002,7 +3014,7 @@ mod test_fmt {
|
||||||
for entry in walkdir::WalkDir::new(&root) {
|
for entry in walkdir::WalkDir::new(&root) {
|
||||||
let entry = entry.unwrap();
|
let entry = entry.unwrap();
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
if path.extension() == Some(&std::ffi::OsStr::new("roc")) {
|
if path.extension() == Some(std::ffi::OsStr::new("roc")) {
|
||||||
count += 1;
|
count += 1;
|
||||||
let src = std::fs::read_to_string(path).unwrap();
|
let src = std::fs::read_to_string(path).unwrap();
|
||||||
println!("Now trying to format {}", path.display());
|
println!("Now trying to format {}", path.display());
|
||||||
|
|
|
@ -66,10 +66,12 @@ impl RegTrait for AArch64FloatReg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct AArch64Assembler {}
|
pub struct AArch64Assembler {}
|
||||||
|
|
||||||
// AArch64Call may need to eventually be split by OS,
|
// AArch64Call may need to eventually be split by OS,
|
||||||
// but I think with how we use it, they may all be the same.
|
// but I think with how we use it, they may all be the same.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct AArch64Call {}
|
pub struct AArch64Call {}
|
||||||
|
|
||||||
const STACK_ALIGNMENT: u8 = 16;
|
const STACK_ALIGNMENT: u8 = 16;
|
||||||
|
@ -281,7 +283,8 @@ impl CallConv<AArch64GeneralReg, AArch64FloatReg, AArch64Assembler> for AArch64C
|
||||||
AArch64Assembler,
|
AArch64Assembler,
|
||||||
AArch64Call,
|
AArch64Call,
|
||||||
>,
|
>,
|
||||||
_args: &'a [Symbol],
|
_dst: &Symbol,
|
||||||
|
_args: &[Symbol],
|
||||||
_arg_layouts: &[Layout<'a>],
|
_arg_layouts: &[Layout<'a>],
|
||||||
_ret_layout: &Layout<'a>,
|
_ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
@ -480,6 +483,70 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn mov_reg64_mem64_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: AArch64GeneralReg,
|
||||||
|
src: AArch64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
) {
|
||||||
|
if offset < 0 {
|
||||||
|
todo!("negative mem offsets for AArch64");
|
||||||
|
} else if offset < (0xFFF << 8) {
|
||||||
|
debug_assert!(offset % 8 == 0);
|
||||||
|
ldr_reg64_imm12(buf, dst, src, (offset as u16) >> 3);
|
||||||
|
} else {
|
||||||
|
todo!("mem offsets over 32k for AArch64");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn mov_mem64_offset32_reg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: AArch64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
src: AArch64GeneralReg,
|
||||||
|
) {
|
||||||
|
if offset < 0 {
|
||||||
|
todo!("negative mem offsets for AArch64");
|
||||||
|
} else if offset < (0xFFF << 8) {
|
||||||
|
debug_assert!(offset % 8 == 0);
|
||||||
|
str_reg64_imm12(buf, src, dst, (offset as u16) >> 3);
|
||||||
|
} else {
|
||||||
|
todo!("mem offsets over 32k for AArch64");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) {
|
||||||
|
debug_assert!(size <= 8);
|
||||||
|
if size == 8 {
|
||||||
|
Self::mov_reg64_base32(buf, dst, offset);
|
||||||
|
} else if size == 4 {
|
||||||
|
todo!("sign extending 4 byte values");
|
||||||
|
} else if size == 2 {
|
||||||
|
todo!("sign extending 2 byte values");
|
||||||
|
} else if size == 1 {
|
||||||
|
todo!("sign extending 1 byte values");
|
||||||
|
} else {
|
||||||
|
internal_error!("Invalid size for sign extension: {}", size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: AArch64GeneralReg, offset: i32, size: u8) {
|
||||||
|
debug_assert!(size <= 8);
|
||||||
|
if size == 8 {
|
||||||
|
Self::mov_reg64_base32(buf, dst, offset);
|
||||||
|
} else if size == 4 {
|
||||||
|
todo!("zero extending 4 byte values");
|
||||||
|
} else if size == 2 {
|
||||||
|
todo!("zero extending 2 byte values");
|
||||||
|
} else if size == 1 {
|
||||||
|
todo!("zero extending 1 byte values");
|
||||||
|
} else {
|
||||||
|
internal_error!("Invalid size for zero extension: {}", size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mov_freg64_stack32(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _offset: i32) {
|
fn mov_freg64_stack32(_buf: &mut Vec<'_, u8>, _dst: AArch64FloatReg, _offset: i32) {
|
||||||
todo!("loading floating point reg from stack for AArch64");
|
todo!("loading floating point reg from stack for AArch64");
|
||||||
|
@ -606,6 +673,16 @@ impl Assembler<AArch64GeneralReg, AArch64FloatReg> for AArch64Assembler {
|
||||||
todo!("registers to float for AArch64");
|
todo!("registers to float for AArch64");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lte_reg64_reg64_reg64(
|
||||||
|
_buf: &mut Vec<'_, u8>,
|
||||||
|
_dst: AArch64GeneralReg,
|
||||||
|
_src1: AArch64GeneralReg,
|
||||||
|
_src2: AArch64GeneralReg,
|
||||||
|
) {
|
||||||
|
todo!("registers less than or equal for AArch64");
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn gte_reg64_reg64_reg64(
|
fn gte_reg64_reg64_reg64(
|
||||||
_buf: &mut Vec<'_, u8>,
|
_buf: &mut Vec<'_, u8>,
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use crate::{single_register_floats, single_register_integers, Backend, Env, Relocation};
|
use crate::{
|
||||||
|
single_register_floats, single_register_int_builtins, single_register_integers, Backend, Env,
|
||||||
|
Relocation,
|
||||||
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{Interns, Symbol};
|
use roc_module::symbol::{Interns, Symbol};
|
||||||
use roc_mono::code_gen_help::CodeGenHelp;
|
use roc_mono::code_gen_help::CodeGenHelp;
|
||||||
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt};
|
use roc_mono::ir::{BranchInfo, JoinPointId, Literal, Param, ProcLayout, SelfRecursive, Stmt};
|
||||||
use roc_mono::layout::{Builtin, Layout};
|
use roc_mono::layout::{Builtin, Layout, TagIdIntType, UnionLayout};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
@ -16,8 +19,10 @@ pub(crate) mod x86_64;
|
||||||
|
|
||||||
use storage::StorageManager;
|
use storage::StorageManager;
|
||||||
|
|
||||||
|
// TODO: on all number functions double check and deal with over/underflow.
|
||||||
|
|
||||||
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>:
|
pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<GeneralReg, FloatReg>>:
|
||||||
Sized
|
Sized + Copy
|
||||||
{
|
{
|
||||||
const BASE_PTR_REG: GeneralReg;
|
const BASE_PTR_REG: GeneralReg;
|
||||||
const STACK_PTR_REG: GeneralReg;
|
const STACK_PTR_REG: GeneralReg;
|
||||||
|
@ -72,7 +77,8 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
|
||||||
fn store_args<'a>(
|
fn store_args<'a>(
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
|
storage_manager: &mut StorageManager<'a, GeneralReg, FloatReg, ASM, Self>,
|
||||||
args: &'a [Symbol],
|
dst: &Symbol,
|
||||||
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
// ret_layout is needed because if it is a complex type, we pass a pointer as the first arg.
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
|
@ -103,7 +109,7 @@ pub trait CallConv<GeneralReg: RegTrait, FloatReg: RegTrait, ASM: Assembler<Gene
|
||||||
/// Thus, some backends will need to use mulitiple instructions to preform a single one of this calls.
|
/// Thus, some backends will need to use mulitiple instructions to preform a single one of this calls.
|
||||||
/// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`.
|
/// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`.
|
||||||
/// dst should always come before sources.
|
/// dst should always come before sources.
|
||||||
pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized {
|
pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized + Copy {
|
||||||
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
fn abs_reg64_reg64(buf: &mut Vec<'_, u8>, dst: GeneralReg, src: GeneralReg);
|
||||||
fn abs_freg64_freg64(
|
fn abs_freg64_freg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
@ -167,6 +173,26 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized {
|
||||||
fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
fn mov_base32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
||||||
fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
|
fn mov_base32_reg64(buf: &mut Vec<'_, u8>, offset: i32, src: GeneralReg);
|
||||||
|
|
||||||
|
fn mov_reg64_mem64_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: GeneralReg,
|
||||||
|
src: GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
);
|
||||||
|
fn mov_mem64_offset32_reg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
src: GeneralReg,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Sign extends the data at `offset` with `size` as it copies it to `dst`
|
||||||
|
/// size must be less than or equal to 8.
|
||||||
|
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32, size: u8);
|
||||||
|
/// Zero extends the data at `offset` with `size` as it copies it to `dst`
|
||||||
|
/// size must be less than or equal to 8.
|
||||||
|
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32, size: u8);
|
||||||
|
|
||||||
fn mov_freg64_stack32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32);
|
fn mov_freg64_stack32(buf: &mut Vec<'_, u8>, dst: FloatReg, offset: i32);
|
||||||
fn mov_reg64_stack32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
|
fn mov_reg64_stack32(buf: &mut Vec<'_, u8>, dst: GeneralReg, offset: i32);
|
||||||
fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
fn mov_stack32_freg64(buf: &mut Vec<'_, u8>, offset: i32, src: FloatReg);
|
||||||
|
@ -217,6 +243,13 @@ pub trait Assembler<GeneralReg: RegTrait, FloatReg: RegTrait>: Sized {
|
||||||
|
|
||||||
fn to_float_freg64_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
|
fn to_float_freg64_freg32(buf: &mut Vec<'_, u8>, dst: FloatReg, src: FloatReg);
|
||||||
|
|
||||||
|
fn lte_reg64_reg64_reg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: GeneralReg,
|
||||||
|
src1: GeneralReg,
|
||||||
|
src2: GeneralReg,
|
||||||
|
);
|
||||||
|
|
||||||
fn gte_reg64_reg64_reg64(
|
fn gte_reg64_reg64_reg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
dst: GeneralReg,
|
dst: GeneralReg,
|
||||||
|
@ -256,7 +289,7 @@ pub struct Backend64Bit<
|
||||||
free_map: MutMap<*const Stmt<'a>, Vec<'a, Symbol>>,
|
free_map: MutMap<*const Stmt<'a>, Vec<'a, Symbol>>,
|
||||||
|
|
||||||
literal_map: MutMap<Symbol, (*const Literal<'a>, *const Layout<'a>)>,
|
literal_map: MutMap<Symbol, (*const Literal<'a>, *const Layout<'a>)>,
|
||||||
join_map: MutMap<JoinPointId, u64>,
|
join_map: MutMap<JoinPointId, Vec<'a, (u64, u64)>>,
|
||||||
|
|
||||||
storage_manager: StorageManager<'a, GeneralReg, FloatReg, ASM, CC>,
|
storage_manager: StorageManager<'a, GeneralReg, FloatReg, ASM, CC>,
|
||||||
}
|
}
|
||||||
|
@ -328,7 +361,6 @@ impl<
|
||||||
self.join_map.clear();
|
self.join_map.clear();
|
||||||
self.free_map.clear();
|
self.free_map.clear();
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
self.helper_proc_symbols.clear();
|
|
||||||
self.storage_manager.reset();
|
self.storage_manager.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,7 +494,7 @@ impl<
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
fn_name: String,
|
fn_name: String,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
@ -479,6 +511,7 @@ impl<
|
||||||
CC::store_args(
|
CC::store_args(
|
||||||
&mut self.buf,
|
&mut self.buf,
|
||||||
&mut self.storage_manager,
|
&mut self.storage_manager,
|
||||||
|
dst,
|
||||||
args,
|
args,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
|
@ -523,17 +556,20 @@ impl<
|
||||||
.storage_manager
|
.storage_manager
|
||||||
.load_to_general_reg(&mut self.buf, cond_symbol);
|
.load_to_general_reg(&mut self.buf, cond_symbol);
|
||||||
|
|
||||||
|
let mut base_storage = self.storage_manager.clone();
|
||||||
|
let mut max_branch_stack_size = 0;
|
||||||
let mut ret_jumps = bumpalo::vec![in self.env.arena];
|
let mut ret_jumps = bumpalo::vec![in self.env.arena];
|
||||||
let mut tmp = bumpalo::vec![in self.env.arena];
|
let mut tmp = bumpalo::vec![in self.env.arena];
|
||||||
for (val, branch_info, stmt) in branches.iter() {
|
for (val, _branch_info, stmt) in branches.iter() {
|
||||||
|
// TODO: look into branch info and if it matters here.
|
||||||
tmp.clear();
|
tmp.clear();
|
||||||
if let BranchInfo::None = branch_info {
|
// Create jump to next branch if cond_sym not equal to value.
|
||||||
// Create jump to next branch if not cond_sym not equal to value.
|
|
||||||
// Since we don't know the offset yet, set it to 0 and overwrite later.
|
// Since we don't know the offset yet, set it to 0 and overwrite later.
|
||||||
let jne_location = self.buf.len();
|
let jne_location = self.buf.len();
|
||||||
let start_offset = ASM::jne_reg64_imm64_imm32(&mut self.buf, cond_reg, *val, 0);
|
let start_offset = ASM::jne_reg64_imm64_imm32(&mut self.buf, cond_reg, *val, 0);
|
||||||
|
|
||||||
// Build all statements in this branch.
|
// Build all statements in this branch. Using storage as from before any branch.
|
||||||
|
self.storage_manager = base_storage.clone();
|
||||||
self.build_stmt(stmt, ret_layout);
|
self.build_stmt(stmt, ret_layout);
|
||||||
|
|
||||||
// Build unconditional jump to the end of this switch.
|
// Build unconditional jump to the end of this switch.
|
||||||
|
@ -549,12 +585,16 @@ impl<
|
||||||
for (i, byte) in tmp.iter().enumerate() {
|
for (i, byte) in tmp.iter().enumerate() {
|
||||||
self.buf[jne_location + i] = *byte;
|
self.buf[jne_location + i] = *byte;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
todo!("Switch: branch info, {:?}", branch_info);
|
// Update important storage information to avoid overwrites.
|
||||||
|
max_branch_stack_size =
|
||||||
|
std::cmp::max(max_branch_stack_size, self.storage_manager.stack_size());
|
||||||
|
base_storage.update_fn_call_stack_size(self.storage_manager.fn_call_stack_size());
|
||||||
}
|
}
|
||||||
}
|
self.storage_manager = base_storage;
|
||||||
let (branch_info, stmt) = default_branch;
|
self.storage_manager
|
||||||
if let BranchInfo::None = branch_info {
|
.update_stack_size(max_branch_stack_size);
|
||||||
|
let (_branch_info, stmt) = default_branch;
|
||||||
self.build_stmt(stmt, ret_layout);
|
self.build_stmt(stmt, ret_layout);
|
||||||
|
|
||||||
// Update all return jumps to jump past the default case.
|
// Update all return jumps to jump past the default case.
|
||||||
|
@ -567,9 +607,6 @@ impl<
|
||||||
ret_offset as u64,
|
ret_offset as u64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
todo!("Switch: branch info, {:?}", branch_info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_join(
|
fn build_join(
|
||||||
|
@ -580,36 +617,41 @@ impl<
|
||||||
remainder: &'a Stmt<'a>,
|
remainder: &'a Stmt<'a>,
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
// Free everything to the stack to make sure they don't get messed up when looping back to this point.
|
||||||
|
// TODO: look into a nicer solution.
|
||||||
|
self.storage_manager.free_all_to_stack(&mut self.buf);
|
||||||
|
|
||||||
// Ensure all the joinpoint parameters have storage locations.
|
// Ensure all the joinpoint parameters have storage locations.
|
||||||
// On jumps to the joinpoint, we will overwrite those locations as a way to "pass parameters" to the joinpoint.
|
// On jumps to the joinpoint, we will overwrite those locations as a way to "pass parameters" to the joinpoint.
|
||||||
self.storage_manager
|
self.storage_manager
|
||||||
.setup_joinpoint(&mut self.buf, id, parameters);
|
.setup_joinpoint(&mut self.buf, id, parameters);
|
||||||
|
|
||||||
// Create jump to remaining.
|
self.join_map.insert(*id, bumpalo::vec![in self.env.arena]);
|
||||||
let jmp_location = self.buf.len();
|
|
||||||
let start_offset = ASM::jmp_imm32(&mut self.buf, 0x1234_5678);
|
// Build remainder of function first. It is what gets run and jumps to join.
|
||||||
|
self.build_stmt(remainder, ret_layout);
|
||||||
|
|
||||||
|
let join_location = self.buf.len() as u64;
|
||||||
|
|
||||||
// Build all statements in body.
|
// Build all statements in body.
|
||||||
self.join_map.insert(*id, self.buf.len() as u64);
|
|
||||||
self.build_stmt(body, ret_layout);
|
self.build_stmt(body, ret_layout);
|
||||||
|
|
||||||
// Overwrite the original jump with the correct offset.
|
// Overwrite the all jumps to the joinpoint with the correct offset.
|
||||||
let mut tmp = bumpalo::vec![in self.env.arena];
|
let mut tmp = bumpalo::vec![in self.env.arena];
|
||||||
self.update_jmp_imm32_offset(
|
for (jmp_location, start_offset) in self
|
||||||
&mut tmp,
|
.join_map
|
||||||
jmp_location as u64,
|
.remove(id)
|
||||||
start_offset as u64,
|
.unwrap_or_else(|| internal_error!("join point not defined"))
|
||||||
self.buf.len() as u64,
|
{
|
||||||
);
|
tmp.clear();
|
||||||
|
self.update_jmp_imm32_offset(&mut tmp, jmp_location, start_offset, join_location);
|
||||||
// Build remainder of function.
|
}
|
||||||
self.build_stmt(remainder, ret_layout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_jump(
|
fn build_jump(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
_ret_layout: &Layout<'a>,
|
_ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
|
@ -619,15 +661,8 @@ impl<
|
||||||
let jmp_location = self.buf.len();
|
let jmp_location = self.buf.len();
|
||||||
let start_offset = ASM::jmp_imm32(&mut self.buf, 0x1234_5678);
|
let start_offset = ASM::jmp_imm32(&mut self.buf, 0x1234_5678);
|
||||||
|
|
||||||
if let Some(offset) = self.join_map.get(id) {
|
if let Some(vec) = self.join_map.get_mut(id) {
|
||||||
let offset = *offset;
|
vec.push((jmp_location as u64, start_offset as u64))
|
||||||
let mut tmp = bumpalo::vec![in self.env.arena];
|
|
||||||
self.update_jmp_imm32_offset(
|
|
||||||
&mut tmp,
|
|
||||||
jmp_location as u64,
|
|
||||||
start_offset as u64,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
internal_error!("Jump: unknown point specified to jump to: {:?}", id);
|
internal_error!("Jump: unknown point specified to jump to: {:?}", id);
|
||||||
}
|
}
|
||||||
|
@ -716,7 +751,7 @@ impl<
|
||||||
|
|
||||||
fn build_eq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &Layout<'a>) {
|
fn build_eq(&mut self, dst: &Symbol, src1: &Symbol, src2: &Symbol, arg_layout: &Layout<'a>) {
|
||||||
match arg_layout {
|
match arg_layout {
|
||||||
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
Layout::Builtin(single_register_int_builtins!()) => {
|
||||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
let src1_reg = self
|
let src1_reg = self
|
||||||
.storage_manager
|
.storage_manager
|
||||||
|
@ -823,6 +858,28 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_num_lte(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
src1: &Symbol,
|
||||||
|
src2: &Symbol,
|
||||||
|
arg_layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
match arg_layout {
|
||||||
|
Layout::Builtin(single_register_int_builtins!()) => {
|
||||||
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
|
let src1_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, src1);
|
||||||
|
let src2_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, src2);
|
||||||
|
ASM::lte_reg64_reg64_reg64(&mut self.buf, dst_reg, src1_reg, src2_reg);
|
||||||
|
}
|
||||||
|
x => todo!("NumLte: layout, {:?}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_num_gte(
|
fn build_num_gte(
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
|
@ -831,7 +888,7 @@ impl<
|
||||||
arg_layout: &Layout<'a>,
|
arg_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
match arg_layout {
|
match arg_layout {
|
||||||
Layout::Builtin(Builtin::Int(IntWidth::I64 | IntWidth::U64)) => {
|
Layout::Builtin(single_register_int_builtins!()) => {
|
||||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
let src1_reg = self
|
let src1_reg = self
|
||||||
.storage_manager
|
.storage_manager
|
||||||
|
@ -845,13 +902,173 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_list_len(&mut self, dst: &Symbol, list: &Symbol) {
|
||||||
|
self.storage_manager.list_len(&mut self.buf, dst, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_list_get_unsafe(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
list: &Symbol,
|
||||||
|
index: &Symbol,
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
let (base_offset, _) = self.storage_manager.stack_offset_and_size(list);
|
||||||
|
let index_reg = self
|
||||||
|
.storage_manager
|
||||||
|
.load_to_general_reg(&mut self.buf, index);
|
||||||
|
let ret_stack_size = ret_layout.stack_size(self.storage_manager.target_info());
|
||||||
|
// TODO: This can be optimized with smarter instructions.
|
||||||
|
// Also can probably be moved into storage manager at least partly.
|
||||||
|
self.storage_manager.with_tmp_general_reg(
|
||||||
|
&mut self.buf,
|
||||||
|
|storage_manager, buf, list_ptr| {
|
||||||
|
ASM::mov_reg64_base32(buf, list_ptr, base_offset as i32);
|
||||||
|
storage_manager.with_tmp_general_reg(buf, |storage_manager, buf, tmp| {
|
||||||
|
ASM::mov_reg64_imm64(buf, tmp, ret_stack_size as i64);
|
||||||
|
ASM::imul_reg64_reg64_reg64(buf, tmp, tmp, index_reg);
|
||||||
|
ASM::add_reg64_reg64_reg64(buf, tmp, tmp, list_ptr);
|
||||||
|
match ret_layout {
|
||||||
|
single_register_integers!() if ret_stack_size == 8 => {
|
||||||
|
let dst_reg = storage_manager.claim_general_reg(buf, dst);
|
||||||
|
ASM::mov_reg64_mem64_offset32(buf, dst_reg, tmp, 0);
|
||||||
|
}
|
||||||
|
x => internal_error!("Loading list element with layout: {:?}", x),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_list_replace_unsafe(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
// We want to delegate to the zig builtin, but it takes some extra parameters.
|
||||||
|
// Firstly, it takes the alignment of the list.
|
||||||
|
// Secondly, it takes the stack size of an element.
|
||||||
|
// Thirdly, it takes a pointer that it will write the output element to.
|
||||||
|
let list = args[0];
|
||||||
|
let list_layout = arg_layouts[0];
|
||||||
|
let index = args[1];
|
||||||
|
let index_layout = arg_layouts[1];
|
||||||
|
let elem = args[2];
|
||||||
|
let elem_layout = arg_layouts[2];
|
||||||
|
|
||||||
|
let u32_layout = &Layout::Builtin(Builtin::Int(IntWidth::U32));
|
||||||
|
let list_alignment = list_layout.alignment_bytes(self.storage_manager.target_info());
|
||||||
|
self.load_literal(
|
||||||
|
&Symbol::DEV_TMP,
|
||||||
|
u32_layout,
|
||||||
|
&Literal::Int(list_alignment as i128),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Have to pass the input element by pointer, so put it on the stack and load it's address.
|
||||||
|
self.storage_manager
|
||||||
|
.ensure_symbol_on_stack(&mut self.buf, &elem);
|
||||||
|
let u64_layout = &Layout::Builtin(Builtin::Int(IntWidth::U64));
|
||||||
|
let (new_elem_offset, _) = self.storage_manager.stack_offset_and_size(&elem);
|
||||||
|
// Load address of output element into register.
|
||||||
|
let reg = self
|
||||||
|
.storage_manager
|
||||||
|
.claim_general_reg(&mut self.buf, &Symbol::DEV_TMP2);
|
||||||
|
ASM::add_reg64_reg64_imm32(&mut self.buf, reg, CC::BASE_PTR_REG, new_elem_offset);
|
||||||
|
|
||||||
|
// Load the elements size.
|
||||||
|
let elem_stack_size = elem_layout.stack_size(self.storage_manager.target_info());
|
||||||
|
self.load_literal(
|
||||||
|
&Symbol::DEV_TMP3,
|
||||||
|
u64_layout,
|
||||||
|
&Literal::Int(elem_stack_size as i128),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setup the return location.
|
||||||
|
let base_offset = self.storage_manager.claim_stack_area(
|
||||||
|
dst,
|
||||||
|
ret_layout.stack_size(self.storage_manager.target_info()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let ret_fields = if let Layout::Struct { field_layouts, .. } = ret_layout {
|
||||||
|
field_layouts
|
||||||
|
} else {
|
||||||
|
internal_error!(
|
||||||
|
"Expected replace to return a struct instead found: {:?}",
|
||||||
|
ret_layout
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only return list and old element.
|
||||||
|
debug_assert_eq!(ret_fields.len(), 2);
|
||||||
|
|
||||||
|
let (out_list_offset, out_elem_offset) = if ret_fields[0] == elem_layout {
|
||||||
|
(
|
||||||
|
base_offset + ret_fields[0].stack_size(self.storage_manager.target_info()) as i32,
|
||||||
|
base_offset,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
base_offset,
|
||||||
|
base_offset + ret_fields[0].stack_size(self.storage_manager.target_info()) as i32,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load address of output element into register.
|
||||||
|
let reg = self
|
||||||
|
.storage_manager
|
||||||
|
.claim_general_reg(&mut self.buf, &Symbol::DEV_TMP4);
|
||||||
|
ASM::add_reg64_reg64_imm32(&mut self.buf, reg, CC::BASE_PTR_REG, out_elem_offset);
|
||||||
|
|
||||||
|
let lowlevel_args = bumpalo::vec![
|
||||||
|
in self.env.arena;
|
||||||
|
list,
|
||||||
|
Symbol::DEV_TMP,
|
||||||
|
index,
|
||||||
|
Symbol::DEV_TMP2,
|
||||||
|
Symbol::DEV_TMP3,
|
||||||
|
Symbol::DEV_TMP4,
|
||||||
|
];
|
||||||
|
let lowlevel_arg_layouts = bumpalo::vec![
|
||||||
|
in self.env.arena;
|
||||||
|
list_layout,
|
||||||
|
*u32_layout,
|
||||||
|
index_layout,
|
||||||
|
*u64_layout,
|
||||||
|
*u64_layout,
|
||||||
|
*u64_layout,
|
||||||
|
];
|
||||||
|
|
||||||
|
self.build_fn_call(
|
||||||
|
&Symbol::DEV_TMP5,
|
||||||
|
bitcode::LIST_REPLACE.to_string(),
|
||||||
|
&lowlevel_args,
|
||||||
|
&lowlevel_arg_layouts,
|
||||||
|
&list_layout,
|
||||||
|
);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP2);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP3);
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP4);
|
||||||
|
|
||||||
|
// Copy from list to the output record.
|
||||||
|
self.storage_manager.copy_symbol_to_stack_offset(
|
||||||
|
&mut self.buf,
|
||||||
|
out_list_offset,
|
||||||
|
&Symbol::DEV_TMP5,
|
||||||
|
&list_layout,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.free_symbol(&Symbol::DEV_TMP5);
|
||||||
|
}
|
||||||
|
|
||||||
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol) {
|
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol) {
|
||||||
// We may not strictly need an instruction here.
|
|
||||||
// What's important is to load the value, and for src and dest to have different Layouts.
|
|
||||||
// This is used for pointer math in refcounting and for pointer equality
|
|
||||||
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
let dst_reg = self.storage_manager.claim_general_reg(&mut self.buf, dst);
|
||||||
let src_reg = self.storage_manager.load_to_general_reg(&mut self.buf, src);
|
self.storage_manager
|
||||||
ASM::mov_reg64_reg64(&mut self.buf, dst_reg, src_reg);
|
.ensure_symbol_on_stack(&mut self.buf, src);
|
||||||
|
let (offset, _) = self.storage_manager.stack_offset_and_size(src);
|
||||||
|
ASM::add_reg64_reg64_imm32(&mut self.buf, dst_reg, CC::BASE_PTR_REG, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]) {
|
fn create_struct(&mut self, sym: &Symbol, layout: &Layout<'a>, fields: &'a [Symbol]) {
|
||||||
|
@ -870,6 +1087,43 @@ impl<
|
||||||
.load_field_at_index(sym, structure, index, field_layouts);
|
.load_field_at_index(sym, structure, index, field_layouts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_union_at_index(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
structure: &Symbol,
|
||||||
|
tag_id: TagIdIntType,
|
||||||
|
index: u64,
|
||||||
|
union_layout: &UnionLayout<'a>,
|
||||||
|
) {
|
||||||
|
match union_layout {
|
||||||
|
UnionLayout::NonRecursive(tag_layouts) | UnionLayout::Recursive(tag_layouts) => {
|
||||||
|
self.storage_manager.load_field_at_index(
|
||||||
|
sym,
|
||||||
|
structure,
|
||||||
|
index,
|
||||||
|
tag_layouts[tag_id as usize],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x => todo!("loading from union type: {:?}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>) {
|
||||||
|
self.storage_manager
|
||||||
|
.load_union_tag_id(&mut self.buf, sym, structure, union_layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tag(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
fields: &'a [Symbol],
|
||||||
|
union_layout: &UnionLayout<'a>,
|
||||||
|
tag_id: TagIdIntType,
|
||||||
|
) {
|
||||||
|
self.storage_manager
|
||||||
|
.create_union(&mut self.buf, sym, union_layout, fields, tag_id)
|
||||||
|
}
|
||||||
|
|
||||||
fn load_literal(&mut self, sym: &Symbol, layout: &Layout<'a>, lit: &Literal<'a>) {
|
fn load_literal(&mut self, sym: &Symbol, layout: &Layout<'a>, lit: &Literal<'a>) {
|
||||||
match (lit, layout) {
|
match (lit, layout) {
|
||||||
(
|
(
|
||||||
|
@ -994,11 +1248,23 @@ impl<
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! single_register_integers {
|
macro_rules! sign_extended_int_builtins {
|
||||||
() => {
|
() => {
|
||||||
Layout::Builtin(
|
Builtin::Int(IntWidth::I8 | IntWidth::I16 | IntWidth::I32 | IntWidth::I64 | IntWidth::I128)
|
||||||
Builtin::Bool
|
};
|
||||||
| Builtin::Int(
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! zero_extended_int_builtins {
|
||||||
|
() => {
|
||||||
|
Builtin::Int(IntWidth::U8 | IntWidth::U16 | IntWidth::U32 | IntWidth::U64 | IntWidth::U128)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! single_register_int_builtins {
|
||||||
|
() => {
|
||||||
|
Builtin::Int(
|
||||||
IntWidth::I8
|
IntWidth::I8
|
||||||
| IntWidth::I16
|
| IntWidth::I16
|
||||||
| IntWidth::I32
|
| IntWidth::I32
|
||||||
|
@ -1007,8 +1273,14 @@ macro_rules! single_register_integers {
|
||||||
| IntWidth::U16
|
| IntWidth::U16
|
||||||
| IntWidth::U32
|
| IntWidth::U32
|
||||||
| IntWidth::U64,
|
| IntWidth::U64,
|
||||||
),
|
)
|
||||||
) | Layout::RecursivePointer
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! single_register_integers {
|
||||||
|
() => {
|
||||||
|
Layout::Builtin(Builtin::Bool | single_register_int_builtins!()) | Layout::RecursivePointer
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
generic64::{Assembler, CallConv, RegTrait},
|
generic64::{Assembler, CallConv, RegTrait},
|
||||||
single_register_floats, single_register_integers, single_register_layouts, Env,
|
sign_extended_int_builtins, single_register_floats, single_register_int_builtins,
|
||||||
|
single_register_integers, single_register_layouts, Env,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
|
@ -9,7 +10,7 @@ use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::{
|
use roc_mono::{
|
||||||
ir::{JoinPointId, Param},
|
ir::{JoinPointId, Param},
|
||||||
layout::{Builtin, Layout},
|
layout::{Builtin, Layout, TagIdIntType, UnionLayout},
|
||||||
};
|
};
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
@ -48,6 +49,9 @@ enum StackStorage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
base_offset: i32,
|
base_offset: i32,
|
||||||
// Size on the stack in bytes.
|
// Size on the stack in bytes.
|
||||||
size: u32,
|
size: u32,
|
||||||
|
// Whether or not the data is need to be sign extended on load.
|
||||||
|
// If not, it must be zero extended.
|
||||||
|
sign_extend: bool,
|
||||||
},
|
},
|
||||||
/// Complex data (lists, unions, structs, str) stored on the stack.
|
/// Complex data (lists, unions, structs, str) stored on the stack.
|
||||||
/// Note, this is also used for referencing a value within a struct/union.
|
/// Note, this is also used for referencing a value within a struct/union.
|
||||||
|
@ -72,6 +76,7 @@ enum Storage<GeneralReg: RegTrait, FloatReg: RegTrait> {
|
||||||
NoData,
|
NoData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct StorageManager<
|
pub struct StorageManager<
|
||||||
'a,
|
'a,
|
||||||
GeneralReg: RegTrait,
|
GeneralReg: RegTrait,
|
||||||
|
@ -177,6 +182,10 @@ impl<
|
||||||
self.fn_call_stack_size = 0;
|
self.fn_call_stack_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn target_info(&self) -> TargetInfo {
|
||||||
|
self.target_info
|
||||||
|
}
|
||||||
|
|
||||||
pub fn stack_size(&self) -> u32 {
|
pub fn stack_size(&self) -> u32 {
|
||||||
self.stack_size
|
self.stack_size
|
||||||
}
|
}
|
||||||
|
@ -323,20 +332,22 @@ impl<
|
||||||
);
|
);
|
||||||
reg
|
reg
|
||||||
}
|
}
|
||||||
Stack(ReferencedPrimitive { base_offset, size })
|
Stack(ReferencedPrimitive {
|
||||||
if base_offset % 8 == 0 && size == 8 =>
|
base_offset,
|
||||||
{
|
size,
|
||||||
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
sign_extend,
|
||||||
|
}) => {
|
||||||
let reg = self.get_general_reg(buf);
|
let reg = self.get_general_reg(buf);
|
||||||
ASM::mov_reg64_base32(buf, reg, base_offset);
|
if sign_extend {
|
||||||
|
ASM::movsx_reg64_base32(buf, reg, base_offset, size as u8);
|
||||||
|
} else {
|
||||||
|
ASM::movzx_reg64_base32(buf, reg, base_offset, size as u8);
|
||||||
|
}
|
||||||
self.general_used_regs.push((reg, *sym));
|
self.general_used_regs.push((reg, *sym));
|
||||||
self.symbol_storage_map.insert(*sym, Reg(General(reg)));
|
self.symbol_storage_map.insert(*sym, Reg(General(reg)));
|
||||||
self.free_reference(sym);
|
self.free_reference(sym);
|
||||||
reg
|
reg
|
||||||
}
|
}
|
||||||
Stack(ReferencedPrimitive { .. }) => {
|
|
||||||
todo!("loading referenced primitives")
|
|
||||||
}
|
|
||||||
Stack(Complex { .. }) => {
|
Stack(Complex { .. }) => {
|
||||||
internal_error!("Cannot load large values into general registers: {}", sym)
|
internal_error!("Cannot load large values into general registers: {}", sym)
|
||||||
}
|
}
|
||||||
|
@ -385,9 +396,9 @@ impl<
|
||||||
);
|
);
|
||||||
reg
|
reg
|
||||||
}
|
}
|
||||||
Stack(ReferencedPrimitive { base_offset, size })
|
Stack(ReferencedPrimitive {
|
||||||
if base_offset % 8 == 0 && size == 8 =>
|
base_offset, size, ..
|
||||||
{
|
}) if base_offset % 8 == 0 && size == 8 => {
|
||||||
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
||||||
let reg = self.get_float_reg(buf);
|
let reg = self.get_float_reg(buf);
|
||||||
ASM::mov_freg64_base32(buf, reg, base_offset);
|
ASM::mov_freg64_base32(buf, reg, base_offset);
|
||||||
|
@ -444,9 +455,9 @@ impl<
|
||||||
debug_assert_eq!(base_offset % 8, 0);
|
debug_assert_eq!(base_offset % 8, 0);
|
||||||
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
||||||
}
|
}
|
||||||
Stack(ReferencedPrimitive { base_offset, size })
|
Stack(ReferencedPrimitive {
|
||||||
if base_offset % 8 == 0 && *size == 8 =>
|
base_offset, size, ..
|
||||||
{
|
}) if base_offset % 8 == 0 && *size == 8 => {
|
||||||
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
||||||
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
ASM::mov_reg64_base32(buf, reg, *base_offset);
|
||||||
}
|
}
|
||||||
|
@ -493,9 +504,9 @@ impl<
|
||||||
debug_assert_eq!(base_offset % 8, 0);
|
debug_assert_eq!(base_offset % 8, 0);
|
||||||
ASM::mov_freg64_base32(buf, reg, *base_offset);
|
ASM::mov_freg64_base32(buf, reg, *base_offset);
|
||||||
}
|
}
|
||||||
Stack(ReferencedPrimitive { base_offset, size })
|
Stack(ReferencedPrimitive {
|
||||||
if base_offset % 8 == 0 && *size == 8 =>
|
base_offset, size, ..
|
||||||
{
|
}) if base_offset % 8 == 0 && *size == 8 => {
|
||||||
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
// The primitive is aligned and the data is exactly 8 bytes, treat it like regular stack.
|
||||||
ASM::mov_freg64_base32(buf, reg, *base_offset);
|
ASM::mov_freg64_base32(buf, reg, *base_offset);
|
||||||
}
|
}
|
||||||
|
@ -522,11 +533,7 @@ impl<
|
||||||
) {
|
) {
|
||||||
debug_assert!(index < field_layouts.len() as u64);
|
debug_assert!(index < field_layouts.len() as u64);
|
||||||
// This must be removed and reinserted for ownership and mutability reasons.
|
// This must be removed and reinserted for ownership and mutability reasons.
|
||||||
let owned_data = if let Some(owned_data) = self.allocation_map.remove(structure) {
|
let owned_data = self.remove_allocation_for_sym(structure);
|
||||||
owned_data
|
|
||||||
} else {
|
|
||||||
internal_error!("Unknown symbol: {}", structure);
|
|
||||||
};
|
|
||||||
self.allocation_map
|
self.allocation_map
|
||||||
.insert(*structure, Rc::clone(&owned_data));
|
.insert(*structure, Rc::clone(&owned_data));
|
||||||
match self.get_storage_for_sym(structure) {
|
match self.get_storage_for_sym(structure) {
|
||||||
|
@ -538,15 +545,19 @@ impl<
|
||||||
data_offset += field_size as i32;
|
data_offset += field_size as i32;
|
||||||
}
|
}
|
||||||
debug_assert!(data_offset < base_offset + size as i32);
|
debug_assert!(data_offset < base_offset + size as i32);
|
||||||
self.allocation_map.insert(*sym, owned_data);
|
|
||||||
let layout = field_layouts[index as usize];
|
let layout = field_layouts[index as usize];
|
||||||
let size = layout.stack_size(self.target_info);
|
let size = layout.stack_size(self.target_info);
|
||||||
|
self.allocation_map.insert(*sym, owned_data);
|
||||||
self.symbol_storage_map.insert(
|
self.symbol_storage_map.insert(
|
||||||
*sym,
|
*sym,
|
||||||
Stack(if is_primitive(&layout) {
|
Stack(if is_primitive(&layout) {
|
||||||
ReferencedPrimitive {
|
ReferencedPrimitive {
|
||||||
base_offset: data_offset,
|
base_offset: data_offset,
|
||||||
size,
|
size,
|
||||||
|
sign_extend: matches!(
|
||||||
|
layout,
|
||||||
|
Layout::Builtin(sign_extended_int_builtins!())
|
||||||
|
),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Complex {
|
Complex {
|
||||||
|
@ -565,6 +576,57 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_union_tag_id(
|
||||||
|
&mut self,
|
||||||
|
_buf: &mut Vec<'a, u8>,
|
||||||
|
sym: &Symbol,
|
||||||
|
structure: &Symbol,
|
||||||
|
union_layout: &UnionLayout<'a>,
|
||||||
|
) {
|
||||||
|
// This must be removed and reinserted for ownership and mutability reasons.
|
||||||
|
let owned_data = self.remove_allocation_for_sym(structure);
|
||||||
|
self.allocation_map
|
||||||
|
.insert(*structure, Rc::clone(&owned_data));
|
||||||
|
match union_layout {
|
||||||
|
UnionLayout::NonRecursive(_) => {
|
||||||
|
let (union_offset, _) = self.stack_offset_and_size(structure);
|
||||||
|
|
||||||
|
let (data_size, data_alignment) =
|
||||||
|
union_layout.data_size_and_alignment(self.target_info);
|
||||||
|
let id_offset = data_size - data_alignment;
|
||||||
|
let id_builtin = union_layout.tag_id_builtin();
|
||||||
|
|
||||||
|
let size = id_builtin.stack_size(self.target_info);
|
||||||
|
self.allocation_map.insert(*sym, owned_data);
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
Stack(ReferencedPrimitive {
|
||||||
|
base_offset: union_offset + id_offset as i32,
|
||||||
|
size,
|
||||||
|
sign_extend: matches!(id_builtin, sign_extended_int_builtins!()),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x => todo!("getting tag id of union with layout ({:?})", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the dst to be the later 64 bits of a list (its length).
|
||||||
|
pub fn list_len(&mut self, _buf: &mut Vec<'a, u8>, dst: &Symbol, list: &Symbol) {
|
||||||
|
let owned_data = self.remove_allocation_for_sym(list);
|
||||||
|
self.allocation_map.insert(*list, Rc::clone(&owned_data));
|
||||||
|
self.allocation_map.insert(*dst, owned_data);
|
||||||
|
let (list_offset, _) = self.stack_offset_and_size(list);
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*dst,
|
||||||
|
Stack(ReferencedPrimitive {
|
||||||
|
base_offset: list_offset + 8,
|
||||||
|
size: 8,
|
||||||
|
sign_extend: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a struct on the stack, moving the data in fields into the struct.
|
/// Creates a struct on the stack, moving the data in fields into the struct.
|
||||||
pub fn create_struct(
|
pub fn create_struct(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -594,11 +656,66 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a union on the stack, moving the data in fields into the union and tagging it.
|
||||||
|
pub fn create_union(
|
||||||
|
&mut self,
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
sym: &Symbol,
|
||||||
|
union_layout: &UnionLayout<'a>,
|
||||||
|
fields: &'a [Symbol],
|
||||||
|
tag_id: TagIdIntType,
|
||||||
|
) {
|
||||||
|
match union_layout {
|
||||||
|
UnionLayout::NonRecursive(field_layouts) => {
|
||||||
|
let (data_size, data_alignment) =
|
||||||
|
union_layout.data_size_and_alignment(self.target_info);
|
||||||
|
let id_offset = data_size - data_alignment;
|
||||||
|
if data_alignment < 8 || data_alignment % 8 != 0 {
|
||||||
|
todo!("small/unaligned tagging");
|
||||||
|
}
|
||||||
|
let base_offset = self.claim_stack_area(sym, data_size);
|
||||||
|
let mut current_offset = base_offset;
|
||||||
|
for (field, field_layout) in
|
||||||
|
fields.iter().zip(field_layouts[tag_id as usize].iter())
|
||||||
|
{
|
||||||
|
self.copy_symbol_to_stack_offset(buf, current_offset, field, field_layout);
|
||||||
|
let field_size = field_layout.stack_size(self.target_info);
|
||||||
|
current_offset += field_size as i32;
|
||||||
|
}
|
||||||
|
self.with_tmp_general_reg(buf, |_symbol_storage, buf, reg| {
|
||||||
|
ASM::mov_reg64_imm64(buf, reg, tag_id as i64);
|
||||||
|
debug_assert!((base_offset + id_offset as i32) % 8 == 0);
|
||||||
|
ASM::mov_base32_reg64(buf, base_offset + id_offset as i32, reg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
x => todo!("creating unions with layout: {:?}", x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies a complex symbol on the stack to the arg pointer.
|
||||||
|
pub fn copy_symbol_to_arg_pointer(
|
||||||
|
&mut self,
|
||||||
|
buf: &mut Vec<'a, u8>,
|
||||||
|
sym: &Symbol,
|
||||||
|
_layout: &Layout<'a>,
|
||||||
|
) {
|
||||||
|
let ret_reg = self.load_to_general_reg(buf, &Symbol::RET_POINTER);
|
||||||
|
let (base_offset, size) = self.stack_offset_and_size(sym);
|
||||||
|
debug_assert!(base_offset % 8 == 0);
|
||||||
|
debug_assert!(size % 8 == 0);
|
||||||
|
self.with_tmp_general_reg(buf, |_storage_manager, buf, tmp_reg| {
|
||||||
|
for i in (0..size as i32).step_by(8) {
|
||||||
|
ASM::mov_reg64_base32(buf, tmp_reg, base_offset + i);
|
||||||
|
ASM::mov_mem64_offset32_reg64(buf, ret_reg, i, tmp_reg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Copies a symbol to the specified stack offset. This is used for things like filling structs.
|
/// Copies a symbol to the specified stack offset. This is used for things like filling structs.
|
||||||
/// The offset is not guarenteed to be perfectly aligned, it follows Roc's alignment plan.
|
/// The offset is not guarenteed to be perfectly aligned, it follows Roc's alignment plan.
|
||||||
/// This means that, for example 2 I32s might be back to back on the stack.
|
/// This means that, for example 2 I32s might be back to back on the stack.
|
||||||
/// Always interact with the stack using aligned 64bit movement.
|
/// Always interact with the stack using aligned 64bit movement.
|
||||||
fn copy_symbol_to_stack_offset(
|
pub fn copy_symbol_to_stack_offset(
|
||||||
&mut self,
|
&mut self,
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
to_offset: i32,
|
to_offset: i32,
|
||||||
|
@ -616,32 +733,33 @@ impl<
|
||||||
let reg = self.load_to_float_reg(buf, sym);
|
let reg = self.load_to_float_reg(buf, sym);
|
||||||
ASM::mov_base32_freg64(buf, to_offset, reg);
|
ASM::mov_base32_freg64(buf, to_offset, reg);
|
||||||
}
|
}
|
||||||
// Layout::Struct(_) if layout.safe_to_memcpy() => {
|
Layout::Builtin(Builtin::Str | Builtin::List(_)) => {
|
||||||
// // self.storage_manager.with_tmp_float_reg(&mut self.buf, |buf, storage, )
|
let (from_offset, _) = self.stack_offset_and_size(sym);
|
||||||
// // if let Some(SymbolStorage::Base {
|
self.with_tmp_general_reg(buf, |_storage_manager, buf, reg| {
|
||||||
// // offset: from_offset,
|
ASM::mov_reg64_base32(buf, reg, from_offset);
|
||||||
// // size,
|
ASM::mov_base32_reg64(buf, to_offset, reg);
|
||||||
// // ..
|
ASM::mov_reg64_base32(buf, reg, from_offset + 8);
|
||||||
// // }) = self.symbol_storage_map.get(sym)
|
ASM::mov_base32_reg64(buf, to_offset + 8, reg);
|
||||||
// // {
|
});
|
||||||
// // debug_assert_eq!(
|
}
|
||||||
// // *size,
|
_ if layout.stack_size(self.target_info) == 0 => {}
|
||||||
// // layout.stack_size(self.target_info),
|
_ if layout.safe_to_memcpy() && layout.stack_size(self.target_info) > 8 => {
|
||||||
// // "expected struct to have same size as data being stored in it"
|
let (from_offset, size) = self.stack_offset_and_size(sym);
|
||||||
// // );
|
debug_assert!(from_offset % 8 == 0);
|
||||||
// // for i in 0..layout.stack_size(self.target_info) as i32 {
|
debug_assert!(size % 8 == 0);
|
||||||
// // ASM::mov_reg64_base32(&mut self.buf, tmp_reg, from_offset + i);
|
debug_assert_eq!(size, layout.stack_size(self.target_info));
|
||||||
// // ASM::mov_base32_reg64(&mut self.buf, to_offset + i, tmp_reg);
|
self.with_tmp_general_reg(buf, |_storage_manager, buf, reg| {
|
||||||
// // }
|
for i in (0..size as i32).step_by(8) {
|
||||||
// todo!()
|
ASM::mov_reg64_base32(buf, reg, from_offset + i);
|
||||||
// } else {
|
ASM::mov_base32_reg64(buf, to_offset + i, reg);
|
||||||
// internal_error!("unknown struct: {:?}", sym);
|
}
|
||||||
// }
|
});
|
||||||
// }
|
}
|
||||||
x => todo!("copying data to the stack with layout, {:?}", x),
|
x => todo!("copying data to the stack with layout, {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
/// Ensures that a register is free. If it is not free, data will be moved to make it free.
|
/// Ensures that a register is free. If it is not free, data will be moved to make it free.
|
||||||
fn ensure_reg_free(
|
fn ensure_reg_free(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -690,6 +808,58 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ensure_symbol_on_stack(&mut self, buf: &mut Vec<'a, u8>, sym: &Symbol) {
|
||||||
|
match self.remove_storage_for_sym(sym) {
|
||||||
|
Reg(reg_storage) => {
|
||||||
|
let base_offset = self.claim_stack_size(8);
|
||||||
|
match reg_storage {
|
||||||
|
General(reg) => ASM::mov_base32_reg64(buf, base_offset, reg),
|
||||||
|
Float(reg) => ASM::mov_base32_freg64(buf, base_offset, reg),
|
||||||
|
}
|
||||||
|
self.symbol_storage_map.insert(
|
||||||
|
*sym,
|
||||||
|
Stack(Primitive {
|
||||||
|
base_offset,
|
||||||
|
reg: Some(reg_storage),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
self.symbol_storage_map.insert(*sym, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees all symbols to the stack setuping up a clean slate.
|
||||||
|
pub fn free_all_to_stack(&mut self, buf: &mut Vec<'a, u8>) {
|
||||||
|
let mut free_list = bumpalo::vec![in self.env.arena];
|
||||||
|
for (sym, storage) in self.symbol_storage_map.iter() {
|
||||||
|
match storage {
|
||||||
|
Reg(reg_storage)
|
||||||
|
| Stack(Primitive {
|
||||||
|
reg: Some(reg_storage),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
free_list.push((*sym, *reg_storage));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (sym, reg_storage) in free_list {
|
||||||
|
match reg_storage {
|
||||||
|
General(reg) => {
|
||||||
|
self.general_free_regs.push(reg);
|
||||||
|
self.general_used_regs.retain(|(r, _)| *r != reg);
|
||||||
|
}
|
||||||
|
Float(reg) => {
|
||||||
|
self.float_free_regs.push(reg);
|
||||||
|
self.float_used_regs.retain(|(r, _)| *r != reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.free_to_stack(buf, &sym, reg_storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Frees `wanted_reg` which is currently owned by `sym` by making sure the value is loaded on the stack.
|
/// Frees `wanted_reg` which is currently owned by `sym` by making sure the value is loaded on the stack.
|
||||||
/// Note, used and free regs are expected to be updated outside of this function.
|
/// Note, used and free regs are expected to be updated outside of this function.
|
||||||
fn free_to_stack(
|
fn free_to_stack(
|
||||||
|
@ -739,9 +909,12 @@ impl<
|
||||||
pub fn stack_offset_and_size(&self, sym: &Symbol) -> (i32, u32) {
|
pub fn stack_offset_and_size(&self, sym: &Symbol) -> (i32, u32) {
|
||||||
match self.get_storage_for_sym(sym) {
|
match self.get_storage_for_sym(sym) {
|
||||||
Stack(Primitive { base_offset, .. }) => (*base_offset, 8),
|
Stack(Primitive { base_offset, .. }) => (*base_offset, 8),
|
||||||
Stack(ReferencedPrimitive { base_offset, size } | Complex { base_offset, size }) => {
|
Stack(
|
||||||
(*base_offset, *size)
|
ReferencedPrimitive {
|
||||||
|
base_offset, size, ..
|
||||||
}
|
}
|
||||||
|
| Complex { base_offset, size },
|
||||||
|
) => (*base_offset, *size),
|
||||||
storage => {
|
storage => {
|
||||||
internal_error!(
|
internal_error!(
|
||||||
"Data not on the stack for sym ({}) with storage ({:?})",
|
"Data not on the stack for sym ({}) with storage ({:?})",
|
||||||
|
@ -775,12 +948,33 @@ impl<
|
||||||
reg: None,
|
reg: None,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
self.allocation_map.insert(*sym, Rc::new((base_offset, 8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies a complex is loaded at the specific base offset.
|
||||||
|
pub fn complex_stack_arg(&mut self, sym: &Symbol, base_offset: i32, size: u32) {
|
||||||
|
self.symbol_storage_map
|
||||||
|
.insert(*sym, Stack(Complex { base_offset, size }));
|
||||||
|
self.allocation_map
|
||||||
|
.insert(*sym, Rc::new((base_offset, size)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies a no data exists.
|
||||||
|
pub fn no_data_arg(&mut self, sym: &Symbol) {
|
||||||
|
self.symbol_storage_map.insert(*sym, NoData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the arg pointer symbol to the specified general reg.
|
/// Loads the arg pointer symbol to the specified general reg.
|
||||||
pub fn ret_pointer_arg(&mut self, reg: GeneralReg) {
|
pub fn ret_pointer_arg(&mut self, reg: GeneralReg) {
|
||||||
self.symbol_storage_map
|
self.symbol_storage_map
|
||||||
.insert(Symbol::RET_POINTER, Reg(General(reg)));
|
.insert(Symbol::RET_POINTER, Reg(General(reg)));
|
||||||
|
self.general_free_regs.retain(|x| *x != reg);
|
||||||
|
self.general_used_regs.push((reg, Symbol::RET_POINTER));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// updates the stack size to the max of its current value and the tmp size needed.
|
||||||
|
pub fn update_stack_size(&mut self, tmp_size: u32) {
|
||||||
|
self.stack_size = max(self.stack_size, tmp_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// updates the function call stack size to the max of its current value and the size need for this call.
|
/// updates the function call stack size to the max of its current value and the size need for this call.
|
||||||
|
@ -794,7 +988,7 @@ impl<
|
||||||
/// Later jumps to the join point can overwrite the stored locations to pass parameters.
|
/// Later jumps to the join point can overwrite the stored locations to pass parameters.
|
||||||
pub fn setup_joinpoint(
|
pub fn setup_joinpoint(
|
||||||
&mut self,
|
&mut self,
|
||||||
buf: &mut Vec<'a, u8>,
|
_buf: &mut Vec<'a, u8>,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
params: &'a [Param<'a>],
|
params: &'a [Param<'a>],
|
||||||
) {
|
) {
|
||||||
|
@ -812,12 +1006,19 @@ impl<
|
||||||
todo!("joinpoints with borrowed parameters");
|
todo!("joinpoints with borrowed parameters");
|
||||||
}
|
}
|
||||||
// Claim a location for every join point parameter to be loaded at.
|
// Claim a location for every join point parameter to be loaded at.
|
||||||
|
// Put everything on the stack for simplicity.
|
||||||
match layout {
|
match layout {
|
||||||
single_register_integers!() => {
|
single_register_layouts!() => {
|
||||||
self.claim_general_reg(buf, symbol);
|
let base_offset = self.claim_stack_size(8);
|
||||||
}
|
self.symbol_storage_map.insert(
|
||||||
single_register_floats!() => {
|
*symbol,
|
||||||
self.claim_float_reg(buf, symbol);
|
Stack(Primitive {
|
||||||
|
base_offset,
|
||||||
|
reg: None,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
self.allocation_map
|
||||||
|
.insert(*symbol, Rc::new((base_offset, 8)));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let stack_size = layout.stack_size(self.target_info);
|
let stack_size = layout.stack_size(self.target_info);
|
||||||
|
@ -839,7 +1040,7 @@ impl<
|
||||||
&mut self,
|
&mut self,
|
||||||
buf: &mut Vec<'a, u8>,
|
buf: &mut Vec<'a, u8>,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
) {
|
) {
|
||||||
// TODO: remove was use here and for current_storage to deal with borrow checker.
|
// TODO: remove was use here and for current_storage to deal with borrow checker.
|
||||||
|
@ -856,28 +1057,45 @@ impl<
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
match wanted_storage {
|
match wanted_storage {
|
||||||
Reg(General(reg)) => {
|
Reg(_) => {
|
||||||
// Ensure the reg is free, if not free it.
|
internal_error!("Register storage is not allowed for jumping to joinpoint")
|
||||||
self.ensure_reg_free(buf, General(*reg));
|
|
||||||
// Copy the value over to the reg.
|
|
||||||
self.load_to_specified_general_reg(buf, sym, *reg)
|
|
||||||
}
|
}
|
||||||
Reg(Float(reg)) => {
|
Stack(Complex { base_offset, .. }) => {
|
||||||
// Ensure the reg is free, if not free it.
|
|
||||||
self.ensure_reg_free(buf, Float(*reg));
|
|
||||||
// Copy the value over to the reg.
|
|
||||||
self.load_to_specified_float_reg(buf, sym, *reg)
|
|
||||||
}
|
|
||||||
Stack(ReferencedPrimitive { base_offset, .. } | Complex { base_offset, .. }) => {
|
|
||||||
// TODO: This might be better not to call.
|
// TODO: This might be better not to call.
|
||||||
// Maybe we want a more memcpy like method to directly get called here.
|
// Maybe we want a more memcpy like method to directly get called here.
|
||||||
// That would also be capable of asserting the size.
|
// That would also be capable of asserting the size.
|
||||||
// Maybe copy stack to stack or something.
|
// Maybe copy stack to stack or something.
|
||||||
self.copy_symbol_to_stack_offset(buf, *base_offset, sym, layout);
|
self.copy_symbol_to_stack_offset(buf, *base_offset, sym, layout);
|
||||||
}
|
}
|
||||||
|
Stack(Primitive {
|
||||||
|
base_offset,
|
||||||
|
reg: None,
|
||||||
|
}) => match layout {
|
||||||
|
single_register_integers!() => {
|
||||||
|
let reg = self.load_to_general_reg(buf, sym);
|
||||||
|
ASM::mov_base32_reg64(buf, *base_offset, reg);
|
||||||
|
}
|
||||||
|
single_register_floats!() => {
|
||||||
|
let reg = self.load_to_float_reg(buf, sym);
|
||||||
|
ASM::mov_base32_freg64(buf, *base_offset, reg);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
internal_error!(
|
||||||
|
"cannot load non-primitive layout ({:?}) to primitive stack location",
|
||||||
|
layout
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
NoData => {}
|
NoData => {}
|
||||||
Stack(Primitive { .. }) => {
|
Stack(Primitive { reg: Some(_), .. }) => {
|
||||||
internal_error!("Primitive stack storage is not allowed for jumping")
|
internal_error!(
|
||||||
|
"primitives with register storage are not allowed for jumping to joinpoint"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Stack(ReferencedPrimitive { .. }) => {
|
||||||
|
internal_error!(
|
||||||
|
"referenced primitive stack storage is not allowed for jumping to joinpoint"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -973,11 +1191,7 @@ impl<
|
||||||
|
|
||||||
/// Frees an reference and release an allocation if it is no longer used.
|
/// Frees an reference and release an allocation if it is no longer used.
|
||||||
fn free_reference(&mut self, sym: &Symbol) {
|
fn free_reference(&mut self, sym: &Symbol) {
|
||||||
let owned_data = if let Some(owned_data) = self.allocation_map.remove(sym) {
|
let owned_data = self.remove_allocation_for_sym(sym);
|
||||||
owned_data
|
|
||||||
} else {
|
|
||||||
internal_error!("Unknown symbol: {:?}", sym);
|
|
||||||
};
|
|
||||||
if Rc::strong_count(&owned_data) == 1 {
|
if Rc::strong_count(&owned_data) == 1 {
|
||||||
self.free_stack_chunk(owned_data.0, owned_data.1);
|
self.free_stack_chunk(owned_data.0, owned_data.1);
|
||||||
}
|
}
|
||||||
|
@ -1060,7 +1274,26 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a value from storage. They index symbol must be defined.
|
#[allow(dead_code)]
|
||||||
|
/// Gets the allocated area for a symbol. The index symbol must be defined.
|
||||||
|
fn get_allocation_for_sym(&self, sym: &Symbol) -> &Rc<(i32, u32)> {
|
||||||
|
if let Some(allocation) = self.allocation_map.get(sym) {
|
||||||
|
allocation
|
||||||
|
} else {
|
||||||
|
internal_error!("Unknown symbol: {:?}", sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes and returns the allocated area for a symbol. They index symbol must be defined.
|
||||||
|
fn remove_allocation_for_sym(&mut self, sym: &Symbol) -> Rc<(i32, u32)> {
|
||||||
|
if let Some(allocation) = self.allocation_map.remove(sym) {
|
||||||
|
allocation
|
||||||
|
} else {
|
||||||
|
internal_error!("Unknown symbol: {:?}", sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a value from storage. The index symbol must be defined.
|
||||||
fn get_storage_for_sym(&self, sym: &Symbol) -> &Storage<GeneralReg, FloatReg> {
|
fn get_storage_for_sym(&self, sym: &Symbol) -> &Storage<GeneralReg, FloatReg> {
|
||||||
if let Some(storage) = self.symbol_storage_map.get(sym) {
|
if let Some(storage) = self.symbol_storage_map.get(sym) {
|
||||||
storage
|
storage
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
|
use crate::generic64::{storage::StorageManager, Assembler, CallConv, RegTrait};
|
||||||
use crate::{
|
use crate::{
|
||||||
single_register_floats, single_register_integers, single_register_layouts, Relocation,
|
single_register_floats, single_register_int_builtins, single_register_integers,
|
||||||
|
single_register_layouts, Relocation,
|
||||||
};
|
};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||||
|
@ -63,8 +64,11 @@ impl RegTrait for X86_64FloatReg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct X86_64Assembler {}
|
pub struct X86_64Assembler {}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct X86_64WindowsFastcall {}
|
pub struct X86_64WindowsFastcall {}
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
pub struct X86_64SystemV {}
|
pub struct X86_64SystemV {}
|
||||||
|
|
||||||
const STACK_ALIGNMENT: u8 = 16;
|
const STACK_ALIGNMENT: u8 = 16;
|
||||||
|
@ -215,6 +219,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
general_i += 1;
|
general_i += 1;
|
||||||
}
|
}
|
||||||
for (layout, sym) in args.iter() {
|
for (layout, sym) in args.iter() {
|
||||||
|
let stack_size = layout.stack_size(TARGET_INFO);
|
||||||
match layout {
|
match layout {
|
||||||
single_register_integers!() => {
|
single_register_integers!() => {
|
||||||
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
if general_i < Self::GENERAL_PARAM_REGS.len() {
|
||||||
|
@ -247,7 +252,14 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
todo!("loading lists and strings args on the stack");
|
todo!("loading lists and strings args on the stack");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x if x.stack_size(TARGET_INFO) == 0 => {}
|
_ if stack_size == 0 => {
|
||||||
|
storage_manager.no_data_arg(sym);
|
||||||
|
}
|
||||||
|
_ if stack_size > 16 => {
|
||||||
|
// TODO: Double check this.
|
||||||
|
storage_manager.complex_stack_arg(sym, arg_offset, stack_size);
|
||||||
|
arg_offset += stack_size as i32;
|
||||||
|
}
|
||||||
x => {
|
x => {
|
||||||
todo!("Loading args with layout {:?}", x);
|
todo!("Loading args with layout {:?}", x);
|
||||||
}
|
}
|
||||||
|
@ -265,19 +277,28 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
X86_64Assembler,
|
X86_64Assembler,
|
||||||
X86_64SystemV,
|
X86_64SystemV,
|
||||||
>,
|
>,
|
||||||
args: &'a [Symbol],
|
dst: &Symbol,
|
||||||
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
||||||
if Self::returns_via_arg_pointer(ret_layout) {
|
|
||||||
// Save space on the stack for the arg we will return.
|
|
||||||
storage_manager
|
|
||||||
.claim_stack_area(&Symbol::RET_POINTER, ret_layout.stack_size(TARGET_INFO));
|
|
||||||
todo!("claim first parama reg for the address");
|
|
||||||
}
|
|
||||||
let mut general_i = 0;
|
let mut general_i = 0;
|
||||||
let mut float_i = 0;
|
let mut float_i = 0;
|
||||||
|
if Self::returns_via_arg_pointer(ret_layout) {
|
||||||
|
// Save space on the stack for the result we will be return.
|
||||||
|
let base_offset =
|
||||||
|
storage_manager.claim_stack_area(dst, ret_layout.stack_size(TARGET_INFO));
|
||||||
|
// Set the first reg to the address base + offset.
|
||||||
|
let ret_reg = Self::GENERAL_PARAM_REGS[general_i];
|
||||||
|
general_i += 1;
|
||||||
|
X86_64Assembler::add_reg64_reg64_imm32(
|
||||||
|
buf,
|
||||||
|
ret_reg,
|
||||||
|
X86_64GeneralReg::RBP,
|
||||||
|
base_offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
for (sym, layout) in args.iter().zip(arg_layouts.iter()) {
|
for (sym, layout) in args.iter().zip(arg_layouts.iter()) {
|
||||||
match layout {
|
match layout {
|
||||||
single_register_integers!() => {
|
single_register_integers!() => {
|
||||||
|
@ -326,7 +347,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
tmp_stack_offset += 8;
|
tmp_stack_offset += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Str) => {
|
Layout::Builtin(Builtin::Str | Builtin::List(_)) => {
|
||||||
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
if general_i + 1 < Self::GENERAL_PARAM_REGS.len() {
|
||||||
let (base_offset, _size) = storage_manager.stack_offset_and_size(sym);
|
let (base_offset, _size) = storage_manager.stack_offset_and_size(sym);
|
||||||
debug_assert_eq!(base_offset % 8, 0);
|
debug_assert_eq!(base_offset % 8, 0);
|
||||||
|
@ -346,6 +367,19 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x if x.stack_size(TARGET_INFO) == 0 => {}
|
x if x.stack_size(TARGET_INFO) == 0 => {}
|
||||||
|
x if x.stack_size(TARGET_INFO) > 16 => {
|
||||||
|
// TODO: Double check this.
|
||||||
|
// Just copy onto the stack.
|
||||||
|
let (base_offset, size) = storage_manager.stack_offset_and_size(sym);
|
||||||
|
debug_assert_eq!(base_offset % 8, 0);
|
||||||
|
storage_manager.with_tmp_general_reg(buf, |_storage_manager, buf, reg| {
|
||||||
|
for i in (0..size as i32).step_by(8) {
|
||||||
|
X86_64Assembler::mov_reg64_base32(buf, reg, base_offset + i);
|
||||||
|
X86_64Assembler::mov_stack32_reg64(buf, tmp_stack_offset + i, reg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tmp_stack_offset += size as i32;
|
||||||
|
}
|
||||||
x => {
|
x => {
|
||||||
todo!("calling with arg type, {:?}", x);
|
todo!("calling with arg type, {:?}", x);
|
||||||
}
|
}
|
||||||
|
@ -381,7 +415,42 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
x if x.stack_size(TARGET_INFO) == 0 => {}
|
x if x.stack_size(TARGET_INFO) == 0 => {}
|
||||||
x => todo!("returning complex type, {:?}", x),
|
x if !Self::returns_via_arg_pointer(x) => {
|
||||||
|
let (base_offset, size) = storage_manager.stack_offset_and_size(sym);
|
||||||
|
debug_assert_eq!(base_offset % 8, 0);
|
||||||
|
if size <= 8 {
|
||||||
|
X86_64Assembler::mov_reg64_base32(
|
||||||
|
buf,
|
||||||
|
Self::GENERAL_RETURN_REGS[0],
|
||||||
|
base_offset,
|
||||||
|
);
|
||||||
|
} else if size <= 16 {
|
||||||
|
X86_64Assembler::mov_reg64_base32(
|
||||||
|
buf,
|
||||||
|
Self::GENERAL_RETURN_REGS[0],
|
||||||
|
base_offset,
|
||||||
|
);
|
||||||
|
X86_64Assembler::mov_reg64_base32(
|
||||||
|
buf,
|
||||||
|
Self::GENERAL_RETURN_REGS[1],
|
||||||
|
base_offset + 8,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
internal_error!(
|
||||||
|
"types that don't return via arg pointer must be less than 16 bytes"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// This is a large type returned via the arg pointer.
|
||||||
|
storage_manager.copy_symbol_to_arg_pointer(buf, sym, layout);
|
||||||
|
// Also set the return reg to the arg pointer.
|
||||||
|
storage_manager.load_to_specified_general_reg(
|
||||||
|
buf,
|
||||||
|
&Symbol::RET_POINTER,
|
||||||
|
Self::GENERAL_RETURN_REGS[0],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +476,29 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Syste
|
||||||
X86_64Assembler::mov_base32_reg64(buf, offset + 8, Self::GENERAL_RETURN_REGS[1]);
|
X86_64Assembler::mov_base32_reg64(buf, offset + 8, Self::GENERAL_RETURN_REGS[1]);
|
||||||
}
|
}
|
||||||
x if x.stack_size(TARGET_INFO) == 0 => {}
|
x if x.stack_size(TARGET_INFO) == 0 => {}
|
||||||
x => todo!("receiving complex return type, {:?}", x),
|
x if !Self::returns_via_arg_pointer(x) => {
|
||||||
|
let size = layout.stack_size(TARGET_INFO);
|
||||||
|
let offset = storage_manager.claim_stack_area(sym, size);
|
||||||
|
if size <= 8 {
|
||||||
|
X86_64Assembler::mov_base32_reg64(buf, offset, Self::GENERAL_RETURN_REGS[0]);
|
||||||
|
} else if size <= 16 {
|
||||||
|
X86_64Assembler::mov_base32_reg64(buf, offset, Self::GENERAL_RETURN_REGS[0]);
|
||||||
|
X86_64Assembler::mov_base32_reg64(
|
||||||
|
buf,
|
||||||
|
offset + 8,
|
||||||
|
Self::GENERAL_RETURN_REGS[1],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
internal_error!(
|
||||||
|
"types that don't return via arg pointer must be less than 16 bytes"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// This should have been recieved via an arg pointer.
|
||||||
|
// That means the value is already loaded onto the stack area we allocated before the call.
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,15 +703,15 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
||||||
X86_64Assembler,
|
X86_64Assembler,
|
||||||
X86_64WindowsFastcall,
|
X86_64WindowsFastcall,
|
||||||
>,
|
>,
|
||||||
args: &'a [Symbol],
|
dst: &Symbol,
|
||||||
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
) {
|
) {
|
||||||
let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
let mut tmp_stack_offset = Self::SHADOW_SPACE_SIZE as i32;
|
||||||
if Self::returns_via_arg_pointer(ret_layout) {
|
if Self::returns_via_arg_pointer(ret_layout) {
|
||||||
// Save space on the stack for the arg we will return.
|
// Save space on the stack for the arg we will return.
|
||||||
storage_manager
|
storage_manager.claim_stack_area(dst, ret_layout.stack_size(TARGET_INFO));
|
||||||
.claim_stack_area(&Symbol::RET_POINTER, ret_layout.stack_size(TARGET_INFO));
|
|
||||||
todo!("claim first parama reg for the address");
|
todo!("claim first parama reg for the address");
|
||||||
}
|
}
|
||||||
for (i, (sym, layout)) in args.iter().zip(arg_layouts.iter()).enumerate() {
|
for (i, (sym, layout)) in args.iter().zip(arg_layouts.iter()).enumerate() {
|
||||||
|
@ -669,7 +760,7 @@ impl CallConv<X86_64GeneralReg, X86_64FloatReg, X86_64Assembler> for X86_64Windo
|
||||||
tmp_stack_offset += 8;
|
tmp_stack_offset += 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Layout::Builtin(Builtin::Str) => {
|
Layout::Builtin(Builtin::Str | Builtin::List(_)) => {
|
||||||
// I think this just needs to be passed on the stack, so not a huge deal.
|
// I think this just needs to be passed on the stack, so not a huge deal.
|
||||||
todo!("Passing str args with Windows fast call");
|
todo!("Passing str args with Windows fast call");
|
||||||
}
|
}
|
||||||
|
@ -988,6 +1079,56 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
mov_base64_offset32_reg64(buf, X86_64GeneralReg::RBP, offset, src)
|
mov_base64_offset32_reg64(buf, X86_64GeneralReg::RBP, offset, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn mov_reg64_mem64_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
src: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
) {
|
||||||
|
mov_reg64_base64_offset32(buf, dst, src, offset)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn mov_mem64_offset32_reg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
src: X86_64GeneralReg,
|
||||||
|
) {
|
||||||
|
mov_base64_offset32_reg64(buf, dst, offset, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn movsx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) {
|
||||||
|
debug_assert!(size <= 8);
|
||||||
|
if size == 8 {
|
||||||
|
Self::mov_reg64_base32(buf, dst, offset);
|
||||||
|
} else if size == 4 {
|
||||||
|
todo!("sign extending 4 byte values");
|
||||||
|
} else if size == 2 {
|
||||||
|
todo!("sign extending 2 byte values");
|
||||||
|
} else if size == 1 {
|
||||||
|
todo!("sign extending 1 byte values");
|
||||||
|
} else {
|
||||||
|
internal_error!("Invalid size for sign extension: {}", size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
fn movzx_reg64_base32(buf: &mut Vec<'_, u8>, dst: X86_64GeneralReg, offset: i32, size: u8) {
|
||||||
|
debug_assert!(size <= 8);
|
||||||
|
if size == 8 {
|
||||||
|
Self::mov_reg64_base32(buf, dst, offset);
|
||||||
|
} else if size == 4 {
|
||||||
|
todo!("zero extending 4 byte values");
|
||||||
|
} else if size == 2 {
|
||||||
|
todo!("zero extending 2 byte values");
|
||||||
|
} else if size == 1 {
|
||||||
|
movzx_reg64_base8_offset32(buf, dst, X86_64GeneralReg::RBP, offset);
|
||||||
|
} else {
|
||||||
|
internal_error!("Invalid size for zero extension: {}", size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mov_freg64_stack32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset: i32) {
|
fn mov_freg64_stack32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset: i32) {
|
||||||
movsd_freg64_base64_offset32(buf, dst, X86_64GeneralReg::RSP, offset)
|
movsd_freg64_base64_offset32(buf, dst, X86_64GeneralReg::RSP, offset)
|
||||||
|
@ -1091,6 +1232,17 @@ impl Assembler<X86_64GeneralReg, X86_64FloatReg> for X86_64Assembler {
|
||||||
cvtsi2sd_freg64_reg64(buf, dst, src);
|
cvtsi2sd_freg64_reg64(buf, dst, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn lte_reg64_reg64_reg64(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
src1: X86_64GeneralReg,
|
||||||
|
src2: X86_64GeneralReg,
|
||||||
|
) {
|
||||||
|
cmp_reg64_reg64(buf, src1, src2);
|
||||||
|
setle_reg64(buf, dst);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn gte_reg64_reg64_reg64(
|
fn gte_reg64_reg64_reg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
@ -1378,6 +1530,27 @@ fn mov_reg64_base64_offset32(
|
||||||
buf.extend(&offset.to_le_bytes());
|
buf.extend(&offset.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `MOVZX r64,r/m8` -> Move r/m8 with zero extention to r64, where m8 references a base + offset.
|
||||||
|
#[inline(always)]
|
||||||
|
fn movzx_reg64_base8_offset32(
|
||||||
|
buf: &mut Vec<'_, u8>,
|
||||||
|
dst: X86_64GeneralReg,
|
||||||
|
base: X86_64GeneralReg,
|
||||||
|
offset: i32,
|
||||||
|
) {
|
||||||
|
let rex = add_rm_extension(base, REX_W);
|
||||||
|
let rex = add_reg_extension(dst, rex);
|
||||||
|
let dst_mod = (dst as u8 % 8) << 3;
|
||||||
|
let base_mod = base as u8 % 8;
|
||||||
|
buf.reserve(9);
|
||||||
|
buf.extend(&[rex, 0x0F, 0xB6, 0x80 + dst_mod + base_mod]);
|
||||||
|
// Using RSP or R12 requires a secondary index byte.
|
||||||
|
if base == X86_64GeneralReg::RSP || base == X86_64GeneralReg::R12 {
|
||||||
|
buf.push(0x24);
|
||||||
|
}
|
||||||
|
buf.extend(&offset.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
/// `MOVSD xmm1,xmm2` -> Move scalar double-precision floating-point value from xmm2 to xmm1 register.
|
/// `MOVSD xmm1,xmm2` -> Move scalar double-precision floating-point value from xmm2 to xmm1 register.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
fn movsd_freg64_freg64(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, src: X86_64FloatReg) {
|
||||||
|
@ -1429,7 +1602,7 @@ fn movsd_freg64_rip_offset32(buf: &mut Vec<'_, u8>, dst: X86_64FloatReg, offset:
|
||||||
buf.extend(&offset.to_le_bytes());
|
buf.extend(&offset.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOVSD r/m64,xmm1` -> Move xmm1 to r/m64. where m64 references the base pionter.
|
/// `MOVSD r/m64,xmm1` -> Move xmm1 to r/m64. where m64 references the base pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movsd_base64_offset32_freg64(
|
fn movsd_base64_offset32_freg64(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
@ -1452,7 +1625,7 @@ fn movsd_base64_offset32_freg64(
|
||||||
buf.extend(&offset.to_le_bytes());
|
buf.extend(&offset.to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MOVSD xmm1,r/m64` -> Move r/m64 to xmm1. where m64 references the base pionter.
|
/// `MOVSD xmm1,r/m64` -> Move r/m64 to xmm1. where m64 references the base pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn movsd_freg64_base64_offset32(
|
fn movsd_freg64_base64_offset32(
|
||||||
buf: &mut Vec<'_, u8>,
|
buf: &mut Vec<'_, u8>,
|
||||||
|
@ -1585,6 +1758,12 @@ fn setl_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
||||||
set_reg64_help(0x9c, buf, reg);
|
set_reg64_help(0x9c, buf, reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `SETLE r/m64` -> Set byte if less or equal (ZF=1 or SF≠ OF).
|
||||||
|
#[inline(always)]
|
||||||
|
fn setle_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
||||||
|
set_reg64_help(0x9e, buf, reg);
|
||||||
|
}
|
||||||
|
|
||||||
/// `SETGE r/m64` -> Set byte if greater or equal (SF=OF).
|
/// `SETGE r/m64` -> Set byte if greater or equal (SF=OF).
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn setge_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
fn setge_reg64(buf: &mut Vec<'_, u8>, reg: X86_64GeneralReg) {
|
||||||
|
@ -2081,6 +2260,35 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_movzx_reg64_base8_offset32() {
|
||||||
|
let arena = bumpalo::Bump::new();
|
||||||
|
let mut buf = bumpalo::vec![in &arena];
|
||||||
|
for ((dst, src, offset), expected) in &[
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::RAX, X86_64GeneralReg::RBP, TEST_I32),
|
||||||
|
vec![0x48, 0x0F, 0xB6, 0x85],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::R15, X86_64GeneralReg::RBP, TEST_I32),
|
||||||
|
vec![0x4C, 0x0F, 0xB6, 0xBD],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::RAX, X86_64GeneralReg::RSP, TEST_I32),
|
||||||
|
vec![0x48, 0x0F, 0xB6, 0x84, 0x24],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
(X86_64GeneralReg::R15, X86_64GeneralReg::RSP, TEST_I32),
|
||||||
|
vec![0x4C, 0x0F, 0xB6, 0xBC, 0x24],
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
buf.clear();
|
||||||
|
movzx_reg64_base8_offset32(&mut buf, *dst, *src, *offset);
|
||||||
|
assert_eq!(expected, &buf[..expected.len()]);
|
||||||
|
assert_eq!(TEST_I32.to_le_bytes(), &buf[expected.len()..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mov_reg64_stack32() {
|
fn test_mov_reg64_stack32() {
|
||||||
let arena = bumpalo::Bump::new();
|
let arena = bumpalo::Bump::new();
|
||||||
|
|
|
@ -14,7 +14,7 @@ use roc_mono::ir::{
|
||||||
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
BranchInfo, CallType, Expr, JoinPointId, ListLiteralElement, Literal, Param, Proc, ProcLayout,
|
||||||
SelfRecursive, Stmt,
|
SelfRecursive, Stmt,
|
||||||
};
|
};
|
||||||
use roc_mono::layout::{Builtin, Layout, LayoutId, LayoutIds};
|
use roc_mono::layout::{Builtin, Layout, LayoutId, LayoutIds, TagIdIntType, UnionLayout};
|
||||||
|
|
||||||
mod generic64;
|
mod generic64;
|
||||||
mod object_builder;
|
mod object_builder;
|
||||||
|
@ -233,7 +233,7 @@ trait Backend<'a> {
|
||||||
fn build_jump(
|
fn build_jump(
|
||||||
&mut self,
|
&mut self,
|
||||||
id: &JoinPointId,
|
id: &JoinPointId,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
@ -277,13 +277,7 @@ trait Backend<'a> {
|
||||||
self.load_literal_symbols(arguments);
|
self.load_literal_symbols(arguments);
|
||||||
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
self.build_fn_call(sym, fn_name, arguments, arg_layouts, ret_layout)
|
||||||
} else {
|
} else {
|
||||||
self.build_inline_builtin(
|
self.build_builtin(sym, *func_sym, arguments, arg_layouts, ret_layout)
|
||||||
sym,
|
|
||||||
*func_sym,
|
|
||||||
arguments,
|
|
||||||
arg_layouts,
|
|
||||||
ret_layout,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,6 +315,29 @@ trait Backend<'a> {
|
||||||
} => {
|
} => {
|
||||||
self.load_struct_at_index(sym, structure, *index, field_layouts);
|
self.load_struct_at_index(sym, structure, *index, field_layouts);
|
||||||
}
|
}
|
||||||
|
Expr::UnionAtIndex {
|
||||||
|
structure,
|
||||||
|
tag_id,
|
||||||
|
union_layout,
|
||||||
|
index,
|
||||||
|
} => {
|
||||||
|
self.load_union_at_index(sym, structure, *tag_id, *index, union_layout);
|
||||||
|
}
|
||||||
|
Expr::GetTagId {
|
||||||
|
structure,
|
||||||
|
union_layout,
|
||||||
|
} => {
|
||||||
|
self.get_tag_id(sym, structure, union_layout);
|
||||||
|
}
|
||||||
|
Expr::Tag {
|
||||||
|
tag_layout,
|
||||||
|
tag_id,
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
self.load_literal_symbols(arguments);
|
||||||
|
self.tag(sym, arguments, tag_layout, *tag_id);
|
||||||
|
}
|
||||||
x => todo!("the expression, {:?}", x),
|
x => todo!("the expression, {:?}", x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,6 +518,23 @@ trait Backend<'a> {
|
||||||
);
|
);
|
||||||
self.build_num_to_float(sym, &args[0], &arg_layouts[0], ret_layout)
|
self.build_num_to_float(sym, &args[0], &arg_layouts[0], ret_layout)
|
||||||
}
|
}
|
||||||
|
LowLevel::NumLte => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
2,
|
||||||
|
args.len(),
|
||||||
|
"NumLte: expected to have exactly two argument"
|
||||||
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
arg_layouts[0], arg_layouts[1],
|
||||||
|
"NumLte: expected all arguments of to have the same layout"
|
||||||
|
);
|
||||||
|
debug_assert_eq!(
|
||||||
|
Layout::Builtin(Builtin::Bool),
|
||||||
|
*ret_layout,
|
||||||
|
"NumLte: expected to have return layout of type Bool"
|
||||||
|
);
|
||||||
|
self.build_num_lte(sym, &args[0], &args[1], &arg_layouts[0])
|
||||||
|
}
|
||||||
LowLevel::NumGte => {
|
LowLevel::NumGte => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
2,
|
2,
|
||||||
|
@ -525,6 +559,30 @@ trait Backend<'a> {
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
ret_layout,
|
ret_layout,
|
||||||
),
|
),
|
||||||
|
LowLevel::ListLen => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
1,
|
||||||
|
args.len(),
|
||||||
|
"ListLen: expected to have exactly one argument"
|
||||||
|
);
|
||||||
|
self.build_list_len(sym, &args[0])
|
||||||
|
}
|
||||||
|
LowLevel::ListGetUnsafe => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
2,
|
||||||
|
args.len(),
|
||||||
|
"ListGetUnsafe: expected to have exactly two arguments"
|
||||||
|
);
|
||||||
|
self.build_list_get_unsafe(sym, &args[0], &args[1], ret_layout)
|
||||||
|
}
|
||||||
|
LowLevel::ListReplaceUnsafe => {
|
||||||
|
debug_assert_eq!(
|
||||||
|
3,
|
||||||
|
args.len(),
|
||||||
|
"ListReplaceUnsafe: expected to have exactly three arguments"
|
||||||
|
);
|
||||||
|
self.build_list_replace_unsafe(sym, args, arg_layouts, ret_layout)
|
||||||
|
}
|
||||||
LowLevel::StrConcat => self.build_fn_call(
|
LowLevel::StrConcat => self.build_fn_call(
|
||||||
sym,
|
sym,
|
||||||
bitcode::STR_CONCAT.to_string(),
|
bitcode::STR_CONCAT.to_string(),
|
||||||
|
@ -558,8 +616,9 @@ trait Backend<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// inlines simple builtin functions that do not map directly to a low level
|
/// Builds a builtin functions that do not map directly to a low level
|
||||||
fn build_inline_builtin(
|
/// If the builtin is simple enough, it will be inlined.
|
||||||
|
fn build_builtin(
|
||||||
&mut self,
|
&mut self,
|
||||||
sym: &Symbol,
|
sym: &Symbol,
|
||||||
func_sym: Symbol,
|
func_sym: Symbol,
|
||||||
|
@ -585,6 +644,14 @@ trait Backend<'a> {
|
||||||
self.build_eq(sym, &args[0], &Symbol::DEV_TMP, &arg_layouts[0]);
|
self.build_eq(sym, &args[0], &Symbol::DEV_TMP, &arg_layouts[0]);
|
||||||
self.free_symbol(&Symbol::DEV_TMP)
|
self.free_symbol(&Symbol::DEV_TMP)
|
||||||
}
|
}
|
||||||
|
Symbol::LIST_GET | Symbol::LIST_SET | Symbol::LIST_REPLACE => {
|
||||||
|
// TODO: This is probably simple enough to be worth inlining.
|
||||||
|
let layout_id = LayoutIds::default().get(func_sym, ret_layout);
|
||||||
|
let fn_name = self.symbol_to_string(func_sym, layout_id);
|
||||||
|
// Now that the arguments are needed, load them if they are literals.
|
||||||
|
self.load_literal_symbols(args);
|
||||||
|
self.build_fn_call(sym, fn_name, args, arg_layouts, ret_layout)
|
||||||
|
}
|
||||||
_ => todo!("the function, {:?}", func_sym),
|
_ => todo!("the function, {:?}", func_sym),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -595,7 +662,7 @@ trait Backend<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
dst: &Symbol,
|
dst: &Symbol,
|
||||||
fn_name: String,
|
fn_name: String,
|
||||||
args: &'a [Symbol],
|
args: &[Symbol],
|
||||||
arg_layouts: &[Layout<'a>],
|
arg_layouts: &[Layout<'a>],
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
@ -633,6 +700,15 @@ trait Backend<'a> {
|
||||||
ret_layout: &Layout<'a>,
|
ret_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// build_num_lte stores the result of `src1 <= src2` into dst.
|
||||||
|
fn build_num_lte(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
src1: &Symbol,
|
||||||
|
src2: &Symbol,
|
||||||
|
arg_layout: &Layout<'a>,
|
||||||
|
);
|
||||||
|
|
||||||
/// build_num_gte stores the result of `src1 >= src2` into dst.
|
/// build_num_gte stores the result of `src1 >= src2` into dst.
|
||||||
fn build_num_gte(
|
fn build_num_gte(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -642,6 +718,27 @@ trait Backend<'a> {
|
||||||
arg_layout: &Layout<'a>,
|
arg_layout: &Layout<'a>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// build_list_len returns the length of a list.
|
||||||
|
fn build_list_len(&mut self, dst: &Symbol, list: &Symbol);
|
||||||
|
|
||||||
|
/// build_list_get_unsafe loads the element from the list at the index.
|
||||||
|
fn build_list_get_unsafe(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
list: &Symbol,
|
||||||
|
index: &Symbol,
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// build_list_replace_unsafe returns the old element and new list with the list having the new element inserted.
|
||||||
|
fn build_list_replace_unsafe(
|
||||||
|
&mut self,
|
||||||
|
dst: &Symbol,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
arg_layouts: &[Layout<'a>],
|
||||||
|
ret_layout: &Layout<'a>,
|
||||||
|
);
|
||||||
|
|
||||||
/// build_refcount_getptr loads the pointer to the reference count of src into dst.
|
/// build_refcount_getptr loads the pointer to the reference count of src into dst.
|
||||||
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol);
|
fn build_ptr_cast(&mut self, dst: &Symbol, src: &Symbol);
|
||||||
|
|
||||||
|
@ -677,6 +774,28 @@ trait Backend<'a> {
|
||||||
field_layouts: &'a [Layout<'a>],
|
field_layouts: &'a [Layout<'a>],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// load_union_at_index loads into `sym` the value at `index` for `tag_id`.
|
||||||
|
fn load_union_at_index(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
structure: &Symbol,
|
||||||
|
tag_id: TagIdIntType,
|
||||||
|
index: u64,
|
||||||
|
union_layout: &UnionLayout<'a>,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// get_tag_id loads the tag id from a the union.
|
||||||
|
fn get_tag_id(&mut self, sym: &Symbol, structure: &Symbol, union_layout: &UnionLayout<'a>);
|
||||||
|
|
||||||
|
/// tag sets the tag for a union.
|
||||||
|
fn tag(
|
||||||
|
&mut self,
|
||||||
|
sym: &Symbol,
|
||||||
|
args: &'a [Symbol],
|
||||||
|
tag_layout: &UnionLayout<'a>,
|
||||||
|
tag_id: TagIdIntType,
|
||||||
|
);
|
||||||
|
|
||||||
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
|
/// return_symbol moves a symbol to the correct return location for the backend and adds a jump to the end of the function.
|
||||||
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>);
|
fn return_symbol(&mut self, sym: &Symbol, layout: &Layout<'a>);
|
||||||
|
|
||||||
|
@ -831,15 +950,16 @@ trait Backend<'a> {
|
||||||
parameters,
|
parameters,
|
||||||
body: continuation,
|
body: continuation,
|
||||||
remainder,
|
remainder,
|
||||||
id,
|
id: JoinPointId(sym),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
join_map.insert(*id, parameters);
|
self.set_last_seen(*sym, stmt);
|
||||||
|
join_map.insert(JoinPointId(*sym), parameters);
|
||||||
for param in *parameters {
|
for param in *parameters {
|
||||||
self.set_last_seen(param.symbol, stmt);
|
self.set_last_seen(param.symbol, stmt);
|
||||||
}
|
}
|
||||||
self.scan_ast(continuation);
|
|
||||||
self.scan_ast(remainder);
|
self.scan_ast(remainder);
|
||||||
|
self.scan_ast(continuation);
|
||||||
}
|
}
|
||||||
Stmt::Jump(JoinPointId(sym), symbols) => {
|
Stmt::Jump(JoinPointId(sym), symbols) => {
|
||||||
if let Some(parameters) = join_map.get(&JoinPointId(*sym)) {
|
if let Some(parameters) = join_map.get(&JoinPointId(*sym)) {
|
||||||
|
@ -848,7 +968,6 @@ trait Backend<'a> {
|
||||||
self.set_last_seen(param.symbol, stmt);
|
self.set_last_seen(param.symbol, stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.set_last_seen(*sym, stmt);
|
|
||||||
for sym in *symbols {
|
for sym in *symbols {
|
||||||
self.set_last_seen(*sym, stmt);
|
self.set_last_seen(*sym, stmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,6 +267,27 @@ fn build_object<'a, B: Backend<'a>>(
|
||||||
helper_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
helper_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// The symbol isn't defined yet and will just be used by other rc procs.
|
||||||
|
let section_id = output.add_section(
|
||||||
|
output.segment_name(StandardSegment::Text).to_vec(),
|
||||||
|
format!(".text.{:x}", sym.as_u64()).as_bytes().to_vec(),
|
||||||
|
SectionKind::Text,
|
||||||
|
);
|
||||||
|
|
||||||
|
let rc_symbol = Symbol {
|
||||||
|
name: fn_name.as_bytes().to_vec(),
|
||||||
|
value: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: SymbolKind::Text,
|
||||||
|
scope: SymbolScope::Linkage,
|
||||||
|
weak: false,
|
||||||
|
section: SymbolSection::Section(section_id),
|
||||||
|
flags: SymbolFlags::None,
|
||||||
|
};
|
||||||
|
let proc_id = output.add_symbol(rc_symbol);
|
||||||
|
helper_names_symbols_procs.push((fn_name, section_id, proc_id, proc));
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
internal_error!("failed to create rc fn for symbol {:?}", sym);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ use crossbeam::deque::{Injector, Stealer, Worker};
|
||||||
use crossbeam::thread;
|
use crossbeam::thread;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use roc_builtins::std::StdLib;
|
use roc_builtins::std::StdLib;
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||||
use roc_can::def::Declaration;
|
use roc_can::def::Declaration;
|
||||||
use roc_can::module::{canonicalize_module_defs, Module};
|
use roc_can::module::{canonicalize_module_defs, Module};
|
||||||
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet};
|
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet};
|
||||||
use roc_constrain::module::{
|
use roc_constrain::module::{
|
||||||
constrain_imports, pre_constrain_imports, ConstrainableImports, Import,
|
constrain_imports, constrain_module, pre_constrain_imports, ConstrainableImports,
|
||||||
|
ExposedModuleTypes, Import, SubsByModule,
|
||||||
};
|
};
|
||||||
use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule};
|
|
||||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
||||||
use roc_module::symbol::{
|
use roc_module::symbol::{
|
||||||
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified,
|
IdentIds, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds, PackageQualified,
|
||||||
|
@ -229,6 +229,7 @@ fn start_phase<'a>(
|
||||||
module,
|
module,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
|
@ -241,6 +242,7 @@ fn start_phase<'a>(
|
||||||
module,
|
module,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
module_timing,
|
module_timing,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
|
@ -391,7 +393,8 @@ struct ConstrainedModule {
|
||||||
module: Module,
|
module: Module,
|
||||||
declarations: Vec<Declaration>,
|
declarations: Vec<Declaration>,
|
||||||
imported_modules: MutMap<ModuleId, Region>,
|
imported_modules: MutMap<ModuleId, Region>,
|
||||||
constraint: Constraint,
|
constraints: Constraints,
|
||||||
|
constraint: ConstraintSoa,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||||
|
@ -728,7 +731,8 @@ enum BuildTask<'a> {
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
imported_symbols: Vec<Import>,
|
imported_symbols: Vec<Import>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
constraint: Constraint,
|
constraints: Constraints,
|
||||||
|
constraint: ConstraintSoa,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
declarations: Vec<Declaration>,
|
declarations: Vec<Declaration>,
|
||||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||||
|
@ -3027,7 +3031,8 @@ impl<'a> BuildTask<'a> {
|
||||||
module: Module,
|
module: Module,
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
constraint: Constraint,
|
constraints: Constraints,
|
||||||
|
constraint: ConstraintSoa,
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
imported_modules: MutMap<ModuleId, Region>,
|
imported_modules: MutMap<ModuleId, Region>,
|
||||||
exposed_types: &mut SubsByModule,
|
exposed_types: &mut SubsByModule,
|
||||||
|
@ -3057,6 +3062,7 @@ impl<'a> BuildTask<'a> {
|
||||||
module,
|
module,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
imported_symbols,
|
imported_symbols,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
declarations,
|
declarations,
|
||||||
|
@ -3073,7 +3079,8 @@ fn run_solve<'a>(
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
mut module_timing: ModuleTiming,
|
mut module_timing: ModuleTiming,
|
||||||
imported_symbols: Vec<Import>,
|
imported_symbols: Vec<Import>,
|
||||||
constraint: Constraint,
|
mut constraints: Constraints,
|
||||||
|
constraint: ConstraintSoa,
|
||||||
mut var_store: VarStore,
|
mut var_store: VarStore,
|
||||||
decls: Vec<Declaration>,
|
decls: Vec<Declaration>,
|
||||||
dep_idents: MutMap<ModuleId, IdentIds>,
|
dep_idents: MutMap<ModuleId, IdentIds>,
|
||||||
|
@ -3084,7 +3091,12 @@ fn run_solve<'a>(
|
||||||
|
|
||||||
// Finish constraining the module by wrapping the existing Constraint
|
// Finish constraining the module by wrapping the existing Constraint
|
||||||
// in the ones we just computed. We can do this off the main thread.
|
// in the ones we just computed. We can do this off the main thread.
|
||||||
let constraint = constrain_imports(imported_symbols, constraint, &mut var_store);
|
let constraint = constrain_imports(
|
||||||
|
&mut constraints,
|
||||||
|
imported_symbols,
|
||||||
|
constraint,
|
||||||
|
&mut var_store,
|
||||||
|
);
|
||||||
|
|
||||||
let constrain_end = SystemTime::now();
|
let constrain_end = SystemTime::now();
|
||||||
|
|
||||||
|
@ -3097,12 +3109,11 @@ fn run_solve<'a>(
|
||||||
..
|
..
|
||||||
} = module;
|
} = module;
|
||||||
|
|
||||||
if false {
|
// TODO
|
||||||
debug_assert!(constraint.validate(), "{:?}", &constraint);
|
// if false { debug_assert!(constraint.validate(), "{:?}", &constraint); }
|
||||||
}
|
|
||||||
|
|
||||||
let (solved_subs, solved_env, problems) =
|
let (solved_subs, solved_env, problems) =
|
||||||
roc_solve::module::run_solve(rigid_variables, constraint, var_store);
|
roc_solve::module::run_solve(&constraints, constraint, rigid_variables, var_store);
|
||||||
|
|
||||||
let exposed_vars_by_symbol: Vec<_> = solved_env
|
let exposed_vars_by_symbol: Vec<_> = solved_env
|
||||||
.vars_by_symbol()
|
.vars_by_symbol()
|
||||||
|
@ -3247,7 +3258,9 @@ fn canonicalize_and_constrain<'a>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let constraint = constrain_module(&module_output.declarations, module_id);
|
let mut constraints = Constraints::new();
|
||||||
|
let constraint =
|
||||||
|
constrain_module(&mut constraints, &module_output.declarations, module_id);
|
||||||
|
|
||||||
let module = Module {
|
let module = Module {
|
||||||
module_id,
|
module_id,
|
||||||
|
@ -3263,6 +3276,7 @@ fn canonicalize_and_constrain<'a>(
|
||||||
declarations: module_output.declarations,
|
declarations: module_output.declarations,
|
||||||
imported_modules,
|
imported_modules,
|
||||||
var_store,
|
var_store,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
ident_ids: module_output.ident_ids,
|
ident_ids: module_output.ident_ids,
|
||||||
dep_idents,
|
dep_idents,
|
||||||
|
@ -3745,6 +3759,7 @@ fn run_task<'a>(
|
||||||
module,
|
module,
|
||||||
module_timing,
|
module_timing,
|
||||||
imported_symbols,
|
imported_symbols,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
ident_ids,
|
ident_ids,
|
||||||
|
@ -3756,6 +3771,7 @@ fn run_task<'a>(
|
||||||
ident_ids,
|
ident_ids,
|
||||||
module_timing,
|
module_timing,
|
||||||
imported_symbols,
|
imported_symbols,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
var_store,
|
var_store,
|
||||||
declarations,
|
declarations,
|
||||||
|
|
|
@ -47,7 +47,7 @@ mod test_load {
|
||||||
|
|
||||||
let src_lines: Vec<&str> = src.split('\n').collect();
|
let src_lines: Vec<&str> = src.split('\n').collect();
|
||||||
let lines = LineInfo::new(src);
|
let lines = LineInfo::new(src);
|
||||||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
let alloc = RocDocAllocator::new(&src_lines, home, interns);
|
||||||
let reports = problems
|
let reports = problems
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|problem| can_problem(&alloc, &lines, filename.clone(), problem).pretty(&alloc));
|
.map(|problem| can_problem(&alloc, &lines, filename.clone(), problem).pretty(&alloc));
|
||||||
|
|
|
@ -884,6 +884,10 @@ define_builtins! {
|
||||||
|
|
||||||
// used in dev backend
|
// used in dev backend
|
||||||
26 DEV_TMP: "#dev_tmp"
|
26 DEV_TMP: "#dev_tmp"
|
||||||
|
27 DEV_TMP2: "#dev_tmp2"
|
||||||
|
28 DEV_TMP3: "#dev_tmp3"
|
||||||
|
29 DEV_TMP4: "#dev_tmp4"
|
||||||
|
30 DEV_TMP5: "#dev_tmp5"
|
||||||
}
|
}
|
||||||
1 NUM: "Num" => {
|
1 NUM: "Num" => {
|
||||||
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
0 NUM_NUM: "Num" imported // the Num.Num type alias
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ir::DestructType;
|
use crate::ir::DestructType;
|
||||||
use roc_collections::all::Index;
|
use roc_collections::all::HumanIndex;
|
||||||
use roc_exhaustive::{
|
use roc_exhaustive::{
|
||||||
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
|
is_useful, Context, Ctor, Error, Guard, Literal, Pattern, RenderAs, TagId, Union,
|
||||||
};
|
};
|
||||||
|
@ -177,7 +177,10 @@ fn to_nonredundant_rows(
|
||||||
vec![Pattern::Ctor(
|
vec![Pattern::Ctor(
|
||||||
union,
|
union,
|
||||||
tag_id,
|
tag_id,
|
||||||
vec![simplify(&loc_pat.value), guard_pattern],
|
// NB: ordering the guard pattern first seems to be better at catching
|
||||||
|
// non-exhaustive constructors in the second argument; see the paper to see if
|
||||||
|
// there is a way to improve this in general.
|
||||||
|
vec![guard_pattern, simplify(&loc_pat.value)],
|
||||||
)]
|
)]
|
||||||
} else {
|
} else {
|
||||||
vec![simplify(&loc_pat.value)]
|
vec![simplify(&loc_pat.value)]
|
||||||
|
@ -189,7 +192,7 @@ fn to_nonredundant_rows(
|
||||||
return Err(Error::Redundant {
|
return Err(Error::Redundant {
|
||||||
overall_region,
|
overall_region,
|
||||||
branch_region: region,
|
branch_region: region,
|
||||||
index: Index::zero_based(checked_rows.len()),
|
index: HumanIndex::zero_based(checked_rows.len()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3882,44 +3882,24 @@ pub fn with_hole<'a>(
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessor {
|
Accessor(accessor_data) => {
|
||||||
|
let field_var = accessor_data.field_var;
|
||||||
|
let fresh_record_symbol = env.unique_symbol();
|
||||||
|
|
||||||
|
let ClosureData {
|
||||||
name,
|
name,
|
||||||
function_var,
|
function_type,
|
||||||
record_var,
|
arguments,
|
||||||
closure_ext_var: _,
|
loc_body,
|
||||||
ext_var,
|
..
|
||||||
field_var,
|
} = accessor_data.to_closure_data(fresh_record_symbol);
|
||||||
field,
|
|
||||||
} => {
|
|
||||||
// IDEA: convert accessor fromt
|
|
||||||
//
|
|
||||||
// .foo
|
|
||||||
//
|
|
||||||
// into
|
|
||||||
//
|
|
||||||
// (\r -> r.foo)
|
|
||||||
let record_symbol = env.unique_symbol();
|
|
||||||
let body = roc_can::expr::Expr::Access {
|
|
||||||
record_var,
|
|
||||||
ext_var,
|
|
||||||
field_var,
|
|
||||||
loc_expr: Box::new(Loc::at_zero(roc_can::expr::Expr::Var(record_symbol))),
|
|
||||||
field,
|
|
||||||
};
|
|
||||||
|
|
||||||
let loc_body = Loc::at_zero(body);
|
|
||||||
|
|
||||||
let arguments = vec![(
|
|
||||||
record_var,
|
|
||||||
Loc::at_zero(roc_can::pattern::Pattern::Identifier(record_symbol)),
|
|
||||||
)];
|
|
||||||
|
|
||||||
match procs.insert_anonymous(
|
match procs.insert_anonymous(
|
||||||
env,
|
env,
|
||||||
name,
|
name,
|
||||||
function_var,
|
function_type,
|
||||||
arguments,
|
arguments,
|
||||||
loc_body,
|
*loc_body,
|
||||||
CapturedSymbols::None,
|
CapturedSymbols::None,
|
||||||
field_var,
|
field_var,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
|
@ -3927,7 +3907,7 @@ pub fn with_hole<'a>(
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let raw_layout = return_on_layout_error!(
|
let raw_layout = return_on_layout_error!(
|
||||||
env,
|
env,
|
||||||
layout_cache.raw_from_var(env.arena, function_var, env.subs)
|
layout_cache.raw_from_var(env.arena, function_type, env.subs)
|
||||||
);
|
);
|
||||||
|
|
||||||
match raw_layout {
|
match raw_layout {
|
||||||
|
@ -5445,6 +5425,18 @@ pub fn from_can<'a>(
|
||||||
|
|
||||||
return from_can(env, variable, cont.value, procs, layout_cache);
|
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
}
|
}
|
||||||
|
roc_can::expr::Expr::Accessor(accessor_data) => {
|
||||||
|
let fresh_record_symbol = env.unique_symbol();
|
||||||
|
register_noncapturing_closure(
|
||||||
|
env,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
*symbol,
|
||||||
|
accessor_data.to_closure_data(fresh_record_symbol),
|
||||||
|
);
|
||||||
|
|
||||||
|
return from_can(env, variable, cont.value, procs, layout_cache);
|
||||||
|
}
|
||||||
roc_can::expr::Expr::Var(original) => {
|
roc_can::expr::Expr::Var(original) => {
|
||||||
// a variable is aliased, e.g.
|
// a variable is aliased, e.g.
|
||||||
//
|
//
|
||||||
|
|
|
@ -74,7 +74,7 @@ impl<'a> RawFunctionLayout<'a> {
|
||||||
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
||||||
RecursionVar { structure, .. } => {
|
RecursionVar { structure, .. } => {
|
||||||
let structure_content = env.subs.get_content_without_compacting(structure);
|
let structure_content = env.subs.get_content_without_compacting(structure);
|
||||||
Self::new_help(env, structure, structure_content.clone())
|
Self::new_help(env, structure, *structure_content)
|
||||||
}
|
}
|
||||||
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
Structure(flat_type) => Self::layout_from_flat_type(env, flat_type),
|
||||||
RangedNumber(typ, _) => Self::from_var(env, typ),
|
RangedNumber(typ, _) => Self::from_var(env, typ),
|
||||||
|
@ -207,7 +207,7 @@ impl<'a> RawFunctionLayout<'a> {
|
||||||
unreachable!("The initial variable of a signature cannot be seen already")
|
unreachable!("The initial variable of a signature cannot be seen already")
|
||||||
} else {
|
} else {
|
||||||
let content = env.subs.get_content_without_compacting(var);
|
let content = env.subs.get_content_without_compacting(var);
|
||||||
Self::new_help(env, var, content.clone())
|
Self::new_help(env, var, *content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -928,7 +928,7 @@ impl<'a> Layout<'a> {
|
||||||
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
FlexVar(_) | RigidVar(_) => Err(LayoutProblem::UnresolvedTypeVar(var)),
|
||||||
RecursionVar { structure, .. } => {
|
RecursionVar { structure, .. } => {
|
||||||
let structure_content = env.subs.get_content_without_compacting(structure);
|
let structure_content = env.subs.get_content_without_compacting(structure);
|
||||||
Self::new_help(env, structure, structure_content.clone())
|
Self::new_help(env, structure, *structure_content)
|
||||||
}
|
}
|
||||||
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
Structure(flat_type) => layout_from_flat_type(env, flat_type),
|
||||||
|
|
||||||
|
@ -968,7 +968,7 @@ impl<'a> Layout<'a> {
|
||||||
Ok(Layout::RecursivePointer)
|
Ok(Layout::RecursivePointer)
|
||||||
} else {
|
} else {
|
||||||
let content = env.subs.get_content_without_compacting(var);
|
let content = env.subs.get_content_without_compacting(var);
|
||||||
Self::new_help(env, var, content.clone())
|
Self::new_help(env, var, *content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,6 +84,16 @@ pub enum Problem {
|
||||||
def_region: Region,
|
def_region: Region,
|
||||||
differing_recursion_region: Region,
|
differing_recursion_region: Region,
|
||||||
},
|
},
|
||||||
|
InvalidExtensionType {
|
||||||
|
region: Region,
|
||||||
|
kind: ExtensionTypeKind,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ExtensionTypeKind {
|
||||||
|
Record,
|
||||||
|
TagUnion,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::solve;
|
use crate::solve;
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::{Constraint as ConstraintSoa, Constraints};
|
||||||
use roc_collections::all::MutMap;
|
use roc_collections::all::MutMap;
|
||||||
use roc_module::ident::Lowercase;
|
use roc_module::ident::Lowercase;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -17,8 +17,9 @@ pub struct SolvedModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_solve(
|
pub fn run_solve(
|
||||||
|
constraints: &Constraints,
|
||||||
|
constraint: ConstraintSoa,
|
||||||
rigid_variables: MutMap<Variable, Lowercase>,
|
rigid_variables: MutMap<Variable, Lowercase>,
|
||||||
constraint: Constraint,
|
|
||||||
var_store: VarStore,
|
var_store: VarStore,
|
||||||
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
) -> (Solved<Subs>, solve::Env, Vec<solve::TypeError>) {
|
||||||
let env = solve::Env::default();
|
let env = solve::Env::default();
|
||||||
|
@ -34,7 +35,7 @@ pub fn run_solve(
|
||||||
let mut problems = Vec::new();
|
let mut problems = Vec::new();
|
||||||
|
|
||||||
// Run the solver to populate Subs.
|
// Run the solver to populate Subs.
|
||||||
let (solved_subs, solved_env) = solve::run(&env, &mut problems, subs, &constraint);
|
let (solved_subs, solved_env) = solve::run(constraints, &env, &mut problems, subs, &constraint);
|
||||||
|
|
||||||
(solved_subs, solved_env, problems)
|
(solved_subs, solved_env, problems)
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5509,4 +5509,68 @@ mod solve_expr {
|
||||||
r#"Id [ A, B, C { a : Str }e ] -> Str"#,
|
r#"Id [ A, B, C { a : Str }e ] -> Str"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lambda_set_within_alias_is_quantified() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
app "test" provides [ effectAlways ] to "./platform"
|
||||||
|
|
||||||
|
Effect a : [ @Effect ({} -> a) ]
|
||||||
|
|
||||||
|
effectAlways : a -> Effect a
|
||||||
|
effectAlways = \x ->
|
||||||
|
inner = \{} -> x
|
||||||
|
|
||||||
|
@Effect inner
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
r#"a -> Effect a"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generalized_accessor_function_applied() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
returnFoo = .foo
|
||||||
|
|
||||||
|
returnFoo { foo: "foo" }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
"Str",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn record_extension_variable_is_alias() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Other a b : { y: a, z: b }
|
||||||
|
|
||||||
|
f : { x : Str }(Other Str Str)
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
r#"{ x : Str, y : Str, z : Str }"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tag_extension_variable_is_alias() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Other : [ B, C ]
|
||||||
|
|
||||||
|
f : [ A ]Other
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
r#"[ A, B, C ]"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ use indoc::indoc;
|
||||||
use roc_std::{RocList, RocStr};
|
use roc_std::{RocList, RocStr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn roc_list_construction() {
|
fn roc_list_construction() {
|
||||||
let list = RocList::from_slice(&[1i64; 23]);
|
let list = RocList::from_slice(&[1i64; 23]);
|
||||||
assert_eq!(&list, &list);
|
assert_eq!(&list, &list);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::helpers::wasm::{assert_evals_to, expect_runtime_error_panic};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use roc_std::RocList;
|
use roc_std::{RocList, RocStr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||||
|
@ -272,7 +272,7 @@ fn empty_record() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn i64_record2_literal() {
|
fn i64_record2_literal() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -299,7 +299,7 @@ fn i64_record2_literal() {
|
||||||
// );
|
// );
|
||||||
// }
|
// }
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn f64_record2_literal() {
|
fn f64_record2_literal() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -401,7 +401,7 @@ fn bool_literal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record() {
|
fn return_record() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -652,7 +652,7 @@ fn optional_field_empty_record() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record_2() {
|
fn return_record_2() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -666,7 +666,7 @@ fn return_record_2() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record_3() {
|
fn return_record_3() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -680,7 +680,7 @@ fn return_record_3() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record_4() {
|
fn return_record_4() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -694,7 +694,7 @@ fn return_record_4() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record_5() {
|
fn return_record_5() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -708,7 +708,7 @@ fn return_record_5() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record_6() {
|
fn return_record_6() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -722,7 +722,7 @@ fn return_record_6() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_record_7() {
|
fn return_record_7() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -792,7 +792,7 @@ fn return_record_float_float_float() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn return_nested_record() {
|
fn return_nested_record() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -1072,3 +1072,18 @@ fn call_with_bad_record_runtime_error() {
|
||||||
"#
|
"#
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||||
|
fn generalized_accessor() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
returnFoo = .foo
|
||||||
|
|
||||||
|
returnFoo { foo: "foo" }
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
RocStr::from("foo"),
|
||||||
|
RocStr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#[cfg(feature = "gen-llvm")]
|
#[cfg(feature = "gen-llvm")]
|
||||||
use crate::helpers::llvm::assert_evals_to;
|
use crate::helpers::llvm::assert_evals_to;
|
||||||
|
|
||||||
// #[cfg(feature = "gen-dev")]
|
#[cfg(feature = "gen-dev")]
|
||||||
// use crate::helpers::dev::assert_evals_to;
|
use crate::helpers::dev::assert_evals_to;
|
||||||
|
|
||||||
#[cfg(feature = "gen-wasm")]
|
#[cfg(feature = "gen-wasm")]
|
||||||
use crate::helpers::wasm::assert_evals_to;
|
use crate::helpers::wasm::assert_evals_to;
|
||||||
|
@ -29,7 +29,7 @@ fn width_and_alignment_u8_u8() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn applied_tag_nothing_ir() {
|
fn applied_tag_nothing_ir() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -49,7 +49,7 @@ fn applied_tag_nothing_ir() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn applied_tag_nothing() {
|
fn applied_tag_nothing() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -69,7 +69,7 @@ fn applied_tag_nothing() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn applied_tag_just() {
|
fn applied_tag_just() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -88,7 +88,7 @@ fn applied_tag_just() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn applied_tag_just_ir() {
|
fn applied_tag_just_ir() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -314,7 +314,7 @@ fn gen_if_float() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn when_on_nothing() {
|
fn when_on_nothing() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -333,7 +333,7 @@ fn when_on_nothing() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn when_on_just() {
|
fn when_on_just() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -352,7 +352,7 @@ fn when_on_just() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn when_on_result() {
|
fn when_on_result() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -371,7 +371,7 @@ fn when_on_result() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn when_on_these() {
|
fn when_on_these() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -393,7 +393,7 @@ fn when_on_these() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn match_on_two_values() {
|
fn match_on_two_values() {
|
||||||
// this will produce a Chain internally
|
// this will produce a Chain internally
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
@ -410,7 +410,7 @@ fn match_on_two_values() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn pair_with_underscore() {
|
fn pair_with_underscore() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -427,7 +427,7 @@ fn pair_with_underscore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||||
fn result_with_underscore() {
|
fn result_with_underscore() {
|
||||||
// This test revealed an issue with hashing Test values
|
// This test revealed an issue with hashing Test values
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::subs::{AliasVariables, Content, FlatType, GetSubsSlice, Subs, UnionTags, Variable};
|
use crate::subs::{
|
||||||
|
AliasVariables, Content, FlatType, GetSubsSlice, Subs, SubsIndex, UnionTags, Variable,
|
||||||
|
};
|
||||||
use crate::types::{name_type_var, RecordField};
|
use crate::types::{name_type_var, RecordField};
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
|
@ -134,20 +136,22 @@ fn find_names_needed(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
opt_name: Some(name),
|
opt_name: Some(name_index),
|
||||||
..
|
..
|
||||||
}
|
}
|
||||||
| FlexVar(Some(name)) => {
|
| FlexVar(Some(name_index)) => {
|
||||||
// This root already has a name. Nothing more to do here!
|
// This root already has a name. Nothing more to do here!
|
||||||
|
|
||||||
// User-defined names are already taken.
|
// User-defined names are already taken.
|
||||||
// We must not accidentally generate names that collide with them!
|
// We must not accidentally generate names that collide with them!
|
||||||
names_taken.insert(name.clone());
|
let name = subs.field_names[name_index.index as usize].clone();
|
||||||
|
names_taken.insert(name);
|
||||||
}
|
}
|
||||||
RigidVar(name) => {
|
RigidVar(name_index) => {
|
||||||
// User-defined names are already taken.
|
// User-defined names are already taken.
|
||||||
// We must not accidentally generate names that collide with them!
|
// We must not accidentally generate names that collide with them!
|
||||||
names_taken.insert(name.clone());
|
let name = subs.field_names[name_index.index as usize].clone();
|
||||||
|
names_taken.insert(name);
|
||||||
}
|
}
|
||||||
Structure(Apply(_, args)) => {
|
Structure(Apply(_, args)) => {
|
||||||
for index in args.into_iter() {
|
for index in args.into_iter() {
|
||||||
|
@ -257,16 +261,19 @@ fn set_root_name(root: Variable, name: Lowercase, subs: &mut Subs) {
|
||||||
|
|
||||||
match old_content {
|
match old_content {
|
||||||
FlexVar(None) => {
|
FlexVar(None) => {
|
||||||
let content = FlexVar(Some(name));
|
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||||
|
let content = FlexVar(Some(name_index));
|
||||||
subs.set_content(root, content);
|
subs.set_content(root, content);
|
||||||
}
|
}
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
opt_name: None,
|
opt_name: None,
|
||||||
structure,
|
structure,
|
||||||
} => {
|
} => {
|
||||||
|
let structure = *structure;
|
||||||
|
let name_index = SubsIndex::push_new(&mut subs.field_names, name);
|
||||||
let content = RecursionVar {
|
let content = RecursionVar {
|
||||||
structure: *structure,
|
structure,
|
||||||
opt_name: Some(name),
|
opt_name: Some(name_index),
|
||||||
};
|
};
|
||||||
subs.set_content(root, content);
|
subs.set_content(root, content);
|
||||||
}
|
}
|
||||||
|
@ -311,11 +318,20 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
use crate::subs::Content::*;
|
use crate::subs::Content::*;
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
FlexVar(Some(name)) => buf.push_str(name.as_str()),
|
FlexVar(Some(name_index)) => {
|
||||||
|
let name = &subs.field_names[name_index.index as usize];
|
||||||
|
buf.push_str(name.as_str())
|
||||||
|
}
|
||||||
FlexVar(None) => buf.push_str(WILDCARD),
|
FlexVar(None) => buf.push_str(WILDCARD),
|
||||||
RigidVar(name) => buf.push_str(name.as_str()),
|
RigidVar(name_index) => {
|
||||||
|
let name = &subs.field_names[name_index.index as usize];
|
||||||
|
buf.push_str(name.as_str())
|
||||||
|
}
|
||||||
RecursionVar { opt_name, .. } => match opt_name {
|
RecursionVar { opt_name, .. } => match opt_name {
|
||||||
Some(name) => buf.push_str(name.as_str()),
|
Some(name_index) => {
|
||||||
|
let name = &subs.field_names[name_index.index as usize];
|
||||||
|
buf.push_str(name.as_str())
|
||||||
|
}
|
||||||
None => buf.push_str(WILDCARD),
|
None => buf.push_str(WILDCARD),
|
||||||
},
|
},
|
||||||
Structure(flat_type) => write_flat_type(env, flat_type, subs, buf, parens),
|
Structure(flat_type) => write_flat_type(env, flat_type, subs, buf, parens),
|
||||||
|
@ -382,7 +398,7 @@ fn write_content(env: &Env, content: &Content, subs: &Subs, buf: &mut String, pa
|
||||||
_ => write_parens!(write_parens, buf, {
|
_ => write_parens!(write_parens, buf, {
|
||||||
write_symbol(env, *symbol, buf);
|
write_symbol(env, *symbol, buf);
|
||||||
|
|
||||||
for var_index in args.into_iter() {
|
for var_index in args.named_type_arguments() {
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
buf.push(' ');
|
buf.push(' ');
|
||||||
write_content(
|
write_content(
|
||||||
|
|
|
@ -257,7 +257,10 @@ impl SolvedType {
|
||||||
// TODO should there be a SolvedType RecursionVar variant?
|
// TODO should there be a SolvedType RecursionVar variant?
|
||||||
Self::from_var_help(subs, recursion_vars, *structure)
|
Self::from_var_help(subs, recursion_vars, *structure)
|
||||||
}
|
}
|
||||||
RigidVar(name) => SolvedType::Rigid(name.clone()),
|
RigidVar(name_index) => {
|
||||||
|
let name = &subs.field_names[name_index.index as usize];
|
||||||
|
SolvedType::Rigid(name.clone())
|
||||||
|
}
|
||||||
Structure(flat_type) => Self::from_flat_type(subs, recursion_vars, flat_type),
|
Structure(flat_type) => Self::from_flat_type(subs, recursion_vars, flat_type),
|
||||||
Alias(symbol, args, actual_var, kind) => {
|
Alias(symbol, args, actual_var, kind) => {
|
||||||
let mut new_args = Vec::with_capacity(args.len());
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
@ -401,7 +404,10 @@ impl SolvedType {
|
||||||
}
|
}
|
||||||
EmptyRecord => SolvedType::EmptyRecord,
|
EmptyRecord => SolvedType::EmptyRecord,
|
||||||
EmptyTagUnion => SolvedType::EmptyTagUnion,
|
EmptyTagUnion => SolvedType::EmptyTagUnion,
|
||||||
Erroneous(problem) => SolvedType::Erroneous(*problem.clone()),
|
Erroneous(problem_index) => {
|
||||||
|
let problem = subs.problems[problem_index.index as usize].clone();
|
||||||
|
SolvedType::Erroneous(problem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::types::{name_type_var, AliasKind, ErrorType, Problem, RecordField, TypeExt};
|
use crate::types::{name_type_var, AliasKind, ErrorType, Problem, RecordField, TypeExt};
|
||||||
use roc_collections::all::{ImMap, ImSet, MutMap, MutSet, SendMap};
|
use roc_collections::all::{ImMap, ImSet, MutSet, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName, Uppercase};
|
use roc_module::ident::{Lowercase, TagName, Uppercase};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -68,7 +68,50 @@ pub struct Subs {
|
||||||
pub field_names: Vec<Lowercase>,
|
pub field_names: Vec<Lowercase>,
|
||||||
pub record_fields: Vec<RecordField<()>>,
|
pub record_fields: Vec<RecordField<()>>,
|
||||||
pub variable_slices: Vec<VariableSubsSlice>,
|
pub variable_slices: Vec<VariableSubsSlice>,
|
||||||
pub tag_name_cache: MutMap<TagName, SubsSlice<TagName>>,
|
pub tag_name_cache: TagNameCache,
|
||||||
|
pub problems: Vec<Problem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct TagNameCache {
|
||||||
|
globals: Vec<Uppercase>,
|
||||||
|
globals_slices: Vec<SubsSlice<TagName>>,
|
||||||
|
/// Currently private tags and closure tags; in the future just closure tags
|
||||||
|
symbols: Vec<Symbol>,
|
||||||
|
symbols_slices: Vec<SubsSlice<TagName>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TagNameCache {
|
||||||
|
pub fn get_mut(&mut self, tag_name: &TagName) -> Option<&mut SubsSlice<TagName>> {
|
||||||
|
match tag_name {
|
||||||
|
TagName::Global(uppercase) => {
|
||||||
|
// force into block
|
||||||
|
match self.globals.iter().position(|u| u == uppercase) {
|
||||||
|
Some(index) => Some(&mut self.globals_slices[index]),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TagName::Private(symbol) | TagName::Closure(symbol) => {
|
||||||
|
match self.symbols.iter().position(|s| s == symbol) {
|
||||||
|
Some(index) => Some(&mut self.symbols_slices[index]),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, tag_name: &TagName, slice: SubsSlice<TagName>) {
|
||||||
|
match tag_name {
|
||||||
|
TagName::Global(uppercase) => {
|
||||||
|
self.globals.push(uppercase.clone());
|
||||||
|
self.globals_slices.push(slice);
|
||||||
|
}
|
||||||
|
TagName::Private(symbol) | TagName::Closure(symbol) => {
|
||||||
|
self.symbols.push(*symbol);
|
||||||
|
self.symbols_slices.push(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Subs {
|
impl Default for Subs {
|
||||||
|
@ -284,6 +327,14 @@ impl<T> SubsIndex<T> {
|
||||||
_marker: std::marker::PhantomData,
|
_marker: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_new(vector: &mut Vec<T>, value: T) -> Self {
|
||||||
|
let index = Self::new(vector.len() as _);
|
||||||
|
|
||||||
|
vector.push(value);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IntoIterator for SubsSlice<T> {
|
impl<T> IntoIterator for SubsSlice<T> {
|
||||||
|
@ -736,8 +787,8 @@ impl Variable {
|
||||||
|
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// This should only ever be called from tests!
|
/// It is not guaranteed that the variable is in bounds.
|
||||||
pub unsafe fn unsafe_test_debug_variable(v: u32) -> Self {
|
pub unsafe fn from_index(v: u32) -> Self {
|
||||||
debug_assert!(v >= Self::NUM_RESERVED_VARS as u32);
|
debug_assert!(v >= Self::NUM_RESERVED_VARS as u32);
|
||||||
Variable(v)
|
Variable(v)
|
||||||
}
|
}
|
||||||
|
@ -1241,14 +1292,15 @@ impl Subs {
|
||||||
|
|
||||||
let mut subs = Subs {
|
let mut subs = Subs {
|
||||||
utable: UnificationTable::default(),
|
utable: UnificationTable::default(),
|
||||||
variables: Default::default(),
|
variables: Vec::new(),
|
||||||
tag_names,
|
tag_names,
|
||||||
field_names: Default::default(),
|
field_names: Vec::new(),
|
||||||
record_fields: Default::default(),
|
record_fields: Vec::new(),
|
||||||
// store an empty slice at the first position
|
// store an empty slice at the first position
|
||||||
// used for "TagOrFunction"
|
// used for "TagOrFunction"
|
||||||
variable_slices: vec![VariableSubsSlice::default()],
|
variable_slices: vec![VariableSubsSlice::default()],
|
||||||
tag_name_cache: MutMap::default(),
|
tag_name_cache: TagNameCache::default(),
|
||||||
|
problems: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE the utable does not (currently) have a with_capacity; using this as the next-best thing
|
// NOTE the utable does not (currently) have a with_capacity; using this as the next-best thing
|
||||||
|
@ -1324,7 +1376,8 @@ impl Subs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rigid_var(&mut self, var: Variable, name: Lowercase) {
|
pub fn rigid_var(&mut self, var: Variable, name: Lowercase) {
|
||||||
let content = Content::RigidVar(name);
|
let name_index = SubsIndex::push_new(&mut self.field_names, name);
|
||||||
|
let content = Content::RigidVar(name_index);
|
||||||
let desc = Descriptor::from(content);
|
let desc = Descriptor::from(content);
|
||||||
|
|
||||||
self.set(var, desc);
|
self.set(var, desc);
|
||||||
|
@ -1432,6 +1485,7 @@ impl Subs {
|
||||||
mapper(self.get_ref_mut(key));
|
mapper(self.get_ref_mut(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank {
|
pub fn get_rank_set_mark(&mut self, key: Variable, mark: Mark) -> Rank {
|
||||||
let l_key = self.utable.get_root_key(key);
|
let l_key = self.utable.get_root_key(key);
|
||||||
|
|
||||||
|
@ -1454,7 +1508,7 @@ impl Subs {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn occurs(&self, var: Variable) -> Result<(), (Variable, Vec<Variable>)> {
|
pub fn occurs(&self, var: Variable) -> Result<(), (Variable, Vec<Variable>)> {
|
||||||
occurs(self, &ImSet::default(), var)
|
occurs(self, &[], var)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_tag_union_recursive(
|
pub fn mark_tag_union_recursive(
|
||||||
|
@ -1680,19 +1734,19 @@ roc_error_macros::assert_sizeof_aarch64!((Variable, Option<Lowercase>), 4 * 8);
|
||||||
roc_error_macros::assert_sizeof_wasm!((Variable, Option<Lowercase>), 4 * 4);
|
roc_error_macros::assert_sizeof_wasm!((Variable, Option<Lowercase>), 4 * 4);
|
||||||
roc_error_macros::assert_sizeof_default!((Variable, Option<Lowercase>), 4 * 8);
|
roc_error_macros::assert_sizeof_default!((Variable, Option<Lowercase>), 4 * 8);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Content {
|
pub enum Content {
|
||||||
/// A type variable which the user did not name in an annotation,
|
/// A type variable which the user did not name in an annotation,
|
||||||
///
|
///
|
||||||
/// When we auto-generate a type var name, e.g. the "a" in (a -> a), we
|
/// When we auto-generate a type var name, e.g. the "a" in (a -> a), we
|
||||||
/// change the Option in here from None to Some.
|
/// change the Option in here from None to Some.
|
||||||
FlexVar(Option<Lowercase>),
|
FlexVar(Option<SubsIndex<Lowercase>>),
|
||||||
/// name given in a user-written annotation
|
/// name given in a user-written annotation
|
||||||
RigidVar(Lowercase),
|
RigidVar(SubsIndex<Lowercase>),
|
||||||
/// name given to a recursion variable
|
/// name given to a recursion variable
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
structure: Variable,
|
structure: Variable,
|
||||||
opt_name: Option<Lowercase>,
|
opt_name: Option<SubsIndex<Lowercase>>,
|
||||||
},
|
},
|
||||||
Structure(FlatType),
|
Structure(FlatType),
|
||||||
Alias(Symbol, AliasVariables, Variable, AliasKind),
|
Alias(Symbol, AliasVariables, Variable, AliasKind),
|
||||||
|
@ -1816,7 +1870,7 @@ impl Content {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum FlatType {
|
pub enum FlatType {
|
||||||
Apply(Symbol, VariableSubsSlice),
|
Apply(Symbol, VariableSubsSlice),
|
||||||
Func(VariableSubsSlice, Variable, Variable),
|
Func(VariableSubsSlice, Variable, Variable),
|
||||||
|
@ -1824,7 +1878,7 @@ pub enum FlatType {
|
||||||
TagUnion(UnionTags, Variable),
|
TagUnion(UnionTags, Variable),
|
||||||
FunctionOrTagUnion(SubsIndex<TagName>, Symbol, Variable),
|
FunctionOrTagUnion(SubsIndex<TagName>, Symbol, Variable),
|
||||||
RecursiveTagUnion(Variable, UnionTags, Variable),
|
RecursiveTagUnion(Variable, UnionTags, Variable),
|
||||||
Erroneous(Box<Problem>),
|
Erroneous(SubsIndex<Problem>),
|
||||||
EmptyRecord,
|
EmptyRecord,
|
||||||
EmptyTagUnion,
|
EmptyTagUnion,
|
||||||
}
|
}
|
||||||
|
@ -2380,7 +2434,7 @@ fn is_empty_record(subs: &Subs, mut var: Variable) -> bool {
|
||||||
|
|
||||||
fn occurs(
|
fn occurs(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
seen: &ImSet<Variable>,
|
seen: &[Variable],
|
||||||
input_var: Variable,
|
input_var: Variable,
|
||||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||||
use self::Content::*;
|
use self::Content::*;
|
||||||
|
@ -2395,9 +2449,9 @@ fn occurs(
|
||||||
FlexVar(_) | RigidVar(_) | RecursionVar { .. } | Error => Ok(()),
|
FlexVar(_) | RigidVar(_) | RecursionVar { .. } | Error => Ok(()),
|
||||||
|
|
||||||
Structure(flat_type) => {
|
Structure(flat_type) => {
|
||||||
let mut new_seen = seen.clone();
|
let mut new_seen = seen.to_owned();
|
||||||
|
|
||||||
new_seen.insert(root_var);
|
new_seen.push(root_var);
|
||||||
|
|
||||||
match flat_type {
|
match flat_type {
|
||||||
Apply(_, args) => {
|
Apply(_, args) => {
|
||||||
|
@ -2446,8 +2500,8 @@ fn occurs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Alias(_, args, _, _) => {
|
Alias(_, args, _, _) => {
|
||||||
let mut new_seen = seen.clone();
|
let mut new_seen = seen.to_owned();
|
||||||
new_seen.insert(root_var);
|
new_seen.push(root_var);
|
||||||
|
|
||||||
for var_index in args.into_iter() {
|
for var_index in args.into_iter() {
|
||||||
let var = subs[var_index];
|
let var = subs[var_index];
|
||||||
|
@ -2457,8 +2511,8 @@ fn occurs(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
RangedNumber(typ, _range_vars) => {
|
RangedNumber(typ, _range_vars) => {
|
||||||
let mut new_seen = seen.clone();
|
let mut new_seen = seen.to_owned();
|
||||||
new_seen.insert(root_var);
|
new_seen.push(root_var);
|
||||||
|
|
||||||
short_circuit_help(subs, root_var, &new_seen, *typ)?;
|
short_circuit_help(subs, root_var, &new_seen, *typ)?;
|
||||||
// _range_vars excluded because they are not explicitly part of the type.
|
// _range_vars excluded because they are not explicitly part of the type.
|
||||||
|
@ -2469,10 +2523,11 @@ fn occurs(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn short_circuit<'a, T>(
|
fn short_circuit<'a, T>(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
root_key: Variable,
|
root_key: Variable,
|
||||||
seen: &ImSet<Variable>,
|
seen: &[Variable],
|
||||||
iter: T,
|
iter: T,
|
||||||
) -> Result<(), (Variable, Vec<Variable>)>
|
) -> Result<(), (Variable, Vec<Variable>)>
|
||||||
where
|
where
|
||||||
|
@ -2485,10 +2540,11 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn short_circuit_help(
|
fn short_circuit_help(
|
||||||
subs: &Subs,
|
subs: &Subs,
|
||||||
root_key: Variable,
|
root_key: Variable,
|
||||||
seen: &ImSet<Variable>,
|
seen: &[Variable],
|
||||||
var: Variable,
|
var: Variable,
|
||||||
) -> Result<(), (Variable, Vec<Variable>)> {
|
) -> Result<(), (Variable, Vec<Variable>)> {
|
||||||
if let Err((v, mut vec)) = occurs(subs, seen, var) {
|
if let Err((v, mut vec)) = occurs(subs, seen, var) {
|
||||||
|
@ -2688,18 +2744,23 @@ fn get_var_names(
|
||||||
match desc.content {
|
match desc.content {
|
||||||
Error | FlexVar(None) => taken_names,
|
Error | FlexVar(None) => taken_names,
|
||||||
|
|
||||||
FlexVar(Some(name)) => {
|
FlexVar(Some(name_index)) => add_name(
|
||||||
add_name(subs, 0, name, var, |name| FlexVar(Some(name)), taken_names)
|
subs,
|
||||||
}
|
0,
|
||||||
|
name_index,
|
||||||
|
var,
|
||||||
|
|name| FlexVar(Some(name)),
|
||||||
|
taken_names,
|
||||||
|
),
|
||||||
|
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
opt_name,
|
opt_name,
|
||||||
structure,
|
structure,
|
||||||
} => match opt_name {
|
} => match opt_name {
|
||||||
Some(name) => add_name(
|
Some(name_index) => add_name(
|
||||||
subs,
|
subs,
|
||||||
0,
|
0,
|
||||||
name,
|
name_index,
|
||||||
var,
|
var,
|
||||||
|name| RecursionVar {
|
|name| RecursionVar {
|
||||||
opt_name: Some(name),
|
opt_name: Some(name),
|
||||||
|
@ -2710,7 +2771,7 @@ fn get_var_names(
|
||||||
None => taken_names,
|
None => taken_names,
|
||||||
},
|
},
|
||||||
|
|
||||||
RigidVar(name) => add_name(subs, 0, name, var, RigidVar, taken_names),
|
RigidVar(name_index) => add_name(subs, 0, name_index, var, RigidVar, taken_names),
|
||||||
|
|
||||||
Alias(_, args, _, _) => args.into_iter().fold(taken_names, |answer, arg_var| {
|
Alias(_, args, _, _) => args.into_iter().fold(taken_names, |answer, arg_var| {
|
||||||
get_var_names(subs, subs[arg_var], answer)
|
get_var_names(subs, subs[arg_var], answer)
|
||||||
|
@ -2800,14 +2861,16 @@ fn get_var_names(
|
||||||
fn add_name<F>(
|
fn add_name<F>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
index: usize,
|
index: usize,
|
||||||
given_name: Lowercase,
|
given_name_index: SubsIndex<Lowercase>,
|
||||||
var: Variable,
|
var: Variable,
|
||||||
content_from_name: F,
|
content_from_name: F,
|
||||||
taken_names: ImMap<Lowercase, Variable>,
|
taken_names: ImMap<Lowercase, Variable>,
|
||||||
) -> ImMap<Lowercase, Variable>
|
) -> ImMap<Lowercase, Variable>
|
||||||
where
|
where
|
||||||
F: FnOnce(Lowercase) -> Content,
|
F: FnOnce(SubsIndex<Lowercase>) -> Content,
|
||||||
{
|
{
|
||||||
|
let given_name = subs.field_names[given_name_index.index as usize].clone();
|
||||||
|
|
||||||
let indexed_name = if index == 0 {
|
let indexed_name = if index == 0 {
|
||||||
given_name.clone()
|
given_name.clone()
|
||||||
} else {
|
} else {
|
||||||
|
@ -2819,7 +2882,9 @@ where
|
||||||
match taken_names.get(&indexed_name) {
|
match taken_names.get(&indexed_name) {
|
||||||
None => {
|
None => {
|
||||||
if indexed_name != given_name {
|
if indexed_name != given_name {
|
||||||
subs.set_content(var, content_from_name(indexed_name.clone()));
|
let indexed_name_index =
|
||||||
|
SubsIndex::push_new(&mut subs.field_names, indexed_name.clone());
|
||||||
|
subs.set_content(var, content_from_name(indexed_name_index));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut answer = taken_names.clone();
|
let mut answer = taken_names.clone();
|
||||||
|
@ -2835,7 +2900,7 @@ where
|
||||||
add_name(
|
add_name(
|
||||||
subs,
|
subs,
|
||||||
index + 1,
|
index + 1,
|
||||||
given_name,
|
given_name_index,
|
||||||
var,
|
var,
|
||||||
content_from_name,
|
content_from_name,
|
||||||
taken_names,
|
taken_names,
|
||||||
|
@ -2846,26 +2911,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn var_to_err_type(subs: &mut Subs, state: &mut ErrorTypeState, var: Variable) -> ErrorType {
|
fn var_to_err_type(subs: &mut Subs, state: &mut ErrorTypeState, var: Variable) -> ErrorType {
|
||||||
let mut desc = subs.get(var);
|
let desc = subs.get(var);
|
||||||
|
|
||||||
if desc.mark == Mark::OCCURS {
|
if desc.mark == Mark::OCCURS {
|
||||||
ErrorType::Infinite
|
ErrorType::Infinite
|
||||||
} else {
|
} else {
|
||||||
subs.set_mark(var, Mark::OCCURS);
|
subs.set_mark(var, Mark::OCCURS);
|
||||||
|
|
||||||
if false {
|
|
||||||
// useful for debugging
|
|
||||||
match desc.content {
|
|
||||||
Content::FlexVar(_) => {
|
|
||||||
desc.content = Content::FlexVar(Some(format!("{:?}", var).into()));
|
|
||||||
}
|
|
||||||
Content::RigidVar(_) => {
|
|
||||||
desc.content = Content::RigidVar(format!("{:?}", var).into());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let err_type = content_to_err_type(subs, state, var, desc.content);
|
let err_type = content_to_err_type(subs, state, var, desc.content);
|
||||||
|
|
||||||
subs.set_mark(var, desc.mark);
|
subs.set_mark(var, desc.mark);
|
||||||
|
@ -2885,15 +2937,20 @@ fn content_to_err_type(
|
||||||
match content {
|
match content {
|
||||||
Structure(flat_type) => flat_type_to_err_type(subs, state, flat_type),
|
Structure(flat_type) => flat_type_to_err_type(subs, state, flat_type),
|
||||||
|
|
||||||
FlexVar(Some(name)) => ErrorType::FlexVar(name),
|
FlexVar(Some(name_index)) => {
|
||||||
|
let name = subs.field_names[name_index.index as usize].clone();
|
||||||
|
ErrorType::FlexVar(name)
|
||||||
|
}
|
||||||
|
|
||||||
FlexVar(opt_name) => {
|
FlexVar(opt_name) => {
|
||||||
let name = match opt_name {
|
let name = match opt_name {
|
||||||
Some(name) => name,
|
Some(name_index) => subs.field_names[name_index.index as usize].clone(),
|
||||||
None => {
|
None => {
|
||||||
|
// set the name so when this variable occurs elsewhere in the type it gets the same name
|
||||||
let name = get_fresh_var_name(state);
|
let name = get_fresh_var_name(state);
|
||||||
|
let name_index = SubsIndex::push_new(&mut subs.field_names, name.clone());
|
||||||
|
|
||||||
subs.set_content(var, FlexVar(Some(name.clone())));
|
subs.set_content(var, FlexVar(Some(name_index)));
|
||||||
|
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
@ -2902,15 +2959,19 @@ fn content_to_err_type(
|
||||||
ErrorType::FlexVar(name)
|
ErrorType::FlexVar(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
RigidVar(name) => ErrorType::RigidVar(name),
|
RigidVar(name_index) => {
|
||||||
|
let name = subs.field_names[name_index.index as usize].clone();
|
||||||
|
ErrorType::RigidVar(name)
|
||||||
|
}
|
||||||
|
|
||||||
RecursionVar { opt_name, .. } => {
|
RecursionVar { opt_name, .. } => {
|
||||||
let name = match opt_name {
|
let name = match opt_name {
|
||||||
Some(name) => name,
|
Some(name_index) => subs.field_names[name_index.index as usize].clone(),
|
||||||
None => {
|
None => {
|
||||||
let name = get_fresh_var_name(state);
|
let name = get_fresh_var_name(state);
|
||||||
|
let name_index = SubsIndex::push_new(&mut subs.field_names, name.clone());
|
||||||
|
|
||||||
subs.set_content(var, FlexVar(Some(name.clone())));
|
subs.set_content(var, FlexVar(Some(name_index)));
|
||||||
|
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
@ -3137,8 +3198,9 @@ fn flat_type_to_err_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Erroneous(problem) => {
|
Erroneous(problem_index) => {
|
||||||
state.problems.push(*problem);
|
let problem = subs.problems[problem_index.index as usize].clone();
|
||||||
|
state.problems.push(problem);
|
||||||
|
|
||||||
ErrorType::Error
|
ErrorType::Error
|
||||||
}
|
}
|
||||||
|
@ -3252,6 +3314,7 @@ struct StorageSubsOffsets {
|
||||||
field_names: u32,
|
field_names: u32,
|
||||||
record_fields: u32,
|
record_fields: u32,
|
||||||
variable_slices: u32,
|
variable_slices: u32,
|
||||||
|
problems: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StorageSubs {
|
impl StorageSubs {
|
||||||
|
@ -3271,6 +3334,7 @@ impl StorageSubs {
|
||||||
field_names: self.subs.field_names.len() as u32,
|
field_names: self.subs.field_names.len() as u32,
|
||||||
record_fields: self.subs.record_fields.len() as u32,
|
record_fields: self.subs.record_fields.len() as u32,
|
||||||
variable_slices: self.subs.variable_slices.len() as u32,
|
variable_slices: self.subs.variable_slices.len() as u32,
|
||||||
|
problems: self.subs.problems.len() as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
let offsets = StorageSubsOffsets {
|
let offsets = StorageSubsOffsets {
|
||||||
|
@ -3280,6 +3344,7 @@ impl StorageSubs {
|
||||||
field_names: target.field_names.len() as u32,
|
field_names: target.field_names.len() as u32,
|
||||||
record_fields: target.record_fields.len() as u32,
|
record_fields: target.record_fields.len() as u32,
|
||||||
variable_slices: target.variable_slices.len() as u32,
|
variable_slices: target.variable_slices.len() as u32,
|
||||||
|
problems: target.problems.len() as u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
// The first Variable::NUM_RESERVED_VARS are the same in every subs,
|
// The first Variable::NUM_RESERVED_VARS are the same in every subs,
|
||||||
|
@ -3324,6 +3389,7 @@ impl StorageSubs {
|
||||||
target.tag_names.extend(self.subs.tag_names);
|
target.tag_names.extend(self.subs.tag_names);
|
||||||
target.field_names.extend(self.subs.field_names);
|
target.field_names.extend(self.subs.field_names);
|
||||||
target.record_fields.extend(self.subs.record_fields);
|
target.record_fields.extend(self.subs.record_fields);
|
||||||
|
target.problems.extend(self.subs.problems);
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
target.utable.len(),
|
target.utable.len(),
|
||||||
|
@ -3340,6 +3406,7 @@ impl StorageSubs {
|
||||||
Self::offset_variable(&offsets, v)
|
Self::offset_variable(&offsets, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_flat_type(offsets: &StorageSubsOffsets, flat_type: &FlatType) -> FlatType {
|
fn offset_flat_type(offsets: &StorageSubsOffsets, flat_type: &FlatType) -> FlatType {
|
||||||
match flat_type {
|
match flat_type {
|
||||||
FlatType::Apply(symbol, arguments) => {
|
FlatType::Apply(symbol, arguments) => {
|
||||||
|
@ -3368,7 +3435,9 @@ impl StorageSubs {
|
||||||
Self::offset_union_tags(offsets, *union_tags),
|
Self::offset_union_tags(offsets, *union_tags),
|
||||||
Self::offset_variable(offsets, *ext),
|
Self::offset_variable(offsets, *ext),
|
||||||
),
|
),
|
||||||
FlatType::Erroneous(problem) => FlatType::Erroneous(problem.clone()),
|
FlatType::Erroneous(problem) => {
|
||||||
|
FlatType::Erroneous(Self::offset_problem(offsets, *problem))
|
||||||
|
}
|
||||||
FlatType::EmptyRecord => FlatType::EmptyRecord,
|
FlatType::EmptyRecord => FlatType::EmptyRecord,
|
||||||
FlatType::EmptyTagUnion => FlatType::EmptyTagUnion,
|
FlatType::EmptyTagUnion => FlatType::EmptyTagUnion,
|
||||||
}
|
}
|
||||||
|
@ -3378,14 +3447,14 @@ impl StorageSubs {
|
||||||
use Content::*;
|
use Content::*;
|
||||||
|
|
||||||
match content {
|
match content {
|
||||||
FlexVar(opt_name) => FlexVar(opt_name.clone()),
|
FlexVar(opt_name) => FlexVar(*opt_name),
|
||||||
RigidVar(name) => RigidVar(name.clone()),
|
RigidVar(name) => RigidVar(*name),
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
structure,
|
structure,
|
||||||
opt_name,
|
opt_name,
|
||||||
} => RecursionVar {
|
} => RecursionVar {
|
||||||
structure: Self::offset_variable(offsets, *structure),
|
structure: Self::offset_variable(offsets, *structure),
|
||||||
opt_name: opt_name.clone(),
|
opt_name: *opt_name,
|
||||||
},
|
},
|
||||||
Structure(flat_type) => Structure(Self::offset_flat_type(offsets, flat_type)),
|
Structure(flat_type) => Structure(Self::offset_flat_type(offsets, flat_type)),
|
||||||
Alias(symbol, alias_variables, actual, kind) => Alias(
|
Alias(symbol, alias_variables, actual, kind) => Alias(
|
||||||
|
@ -3455,6 +3524,15 @@ impl StorageSubs {
|
||||||
|
|
||||||
slice
|
slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn offset_problem(
|
||||||
|
offsets: &StorageSubsOffsets,
|
||||||
|
mut problem_index: SubsIndex<Problem>,
|
||||||
|
) -> SubsIndex<Problem> {
|
||||||
|
problem_index.index += offsets.problems;
|
||||||
|
|
||||||
|
problem_index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::pretty_print::Parens;
|
||||||
use crate::subs::{
|
use crate::subs::{
|
||||||
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
GetSubsSlice, RecordFields, Subs, UnionTags, VarStore, Variable, VariableSubsSlice,
|
||||||
};
|
};
|
||||||
use roc_collections::all::{ImMap, ImSet, Index, MutSet, SendMap};
|
use roc_collections::all::{HumanIndex, ImMap, ImSet, MutSet, SendMap};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::called_via::CalledVia;
|
use roc_module::called_via::CalledVia;
|
||||||
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||||
|
@ -722,10 +722,11 @@ impl Type {
|
||||||
|
|
||||||
/// a shallow dealias, continue until the first constructor is not an alias.
|
/// a shallow dealias, continue until the first constructor is not an alias.
|
||||||
pub fn shallow_dealias(&self) -> &Self {
|
pub fn shallow_dealias(&self) -> &Self {
|
||||||
match self {
|
let mut result = self;
|
||||||
Type::Alias { actual, .. } => actual.shallow_dealias(),
|
while let Type::Alias { actual, .. } = result {
|
||||||
_ => self,
|
result = actual;
|
||||||
}
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instantiate_aliases(
|
pub fn instantiate_aliases(
|
||||||
|
@ -1203,14 +1204,14 @@ pub struct TagUnionStructure<'a> {
|
||||||
pub enum PReason {
|
pub enum PReason {
|
||||||
TypedArg {
|
TypedArg {
|
||||||
opt_name: Option<Symbol>,
|
opt_name: Option<Symbol>,
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
},
|
},
|
||||||
WhenMatch {
|
WhenMatch {
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
},
|
},
|
||||||
TagArg {
|
TagArg {
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
},
|
},
|
||||||
PatternGuard,
|
PatternGuard,
|
||||||
OptionalField,
|
OptionalField,
|
||||||
|
@ -1219,12 +1220,12 @@ pub enum PReason {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum AnnotationSource {
|
pub enum AnnotationSource {
|
||||||
TypedIfBranch {
|
TypedIfBranch {
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
num_branches: usize,
|
num_branches: usize,
|
||||||
region: Region,
|
region: Region,
|
||||||
},
|
},
|
||||||
TypedWhenBranch {
|
TypedWhenBranch {
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
region: Region,
|
region: Region,
|
||||||
},
|
},
|
||||||
TypedBody {
|
TypedBody {
|
||||||
|
@ -1246,7 +1247,7 @@ impl AnnotationSource {
|
||||||
pub enum Reason {
|
pub enum Reason {
|
||||||
FnArg {
|
FnArg {
|
||||||
name: Option<Symbol>,
|
name: Option<Symbol>,
|
||||||
arg_index: Index,
|
arg_index: HumanIndex,
|
||||||
},
|
},
|
||||||
FnCall {
|
FnCall {
|
||||||
name: Option<Symbol>,
|
name: Option<Symbol>,
|
||||||
|
@ -1254,28 +1255,28 @@ pub enum Reason {
|
||||||
},
|
},
|
||||||
LowLevelOpArg {
|
LowLevelOpArg {
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
arg_index: Index,
|
arg_index: HumanIndex,
|
||||||
},
|
},
|
||||||
ForeignCallArg {
|
ForeignCallArg {
|
||||||
foreign_symbol: ForeignSymbol,
|
foreign_symbol: ForeignSymbol,
|
||||||
arg_index: Index,
|
arg_index: HumanIndex,
|
||||||
},
|
},
|
||||||
FloatLiteral,
|
FloatLiteral,
|
||||||
IntLiteral,
|
IntLiteral,
|
||||||
NumLiteral,
|
NumLiteral,
|
||||||
StrInterpolation,
|
StrInterpolation,
|
||||||
WhenBranch {
|
WhenBranch {
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
},
|
},
|
||||||
WhenGuard,
|
WhenGuard,
|
||||||
ExpectCondition,
|
ExpectCondition,
|
||||||
IfCondition,
|
IfCondition,
|
||||||
IfBranch {
|
IfBranch {
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
total_branches: usize,
|
total_branches: usize,
|
||||||
},
|
},
|
||||||
ElemInList {
|
ElemInList {
|
||||||
index: Index,
|
index: HumanIndex,
|
||||||
},
|
},
|
||||||
RecordUpdateValue(Lowercase),
|
RecordUpdateValue(Lowercase),
|
||||||
RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>),
|
RecordUpdateKeys(Symbol, SendMap<Lowercase, Region>),
|
||||||
|
|
|
@ -331,7 +331,7 @@ fn unify_alias(
|
||||||
}
|
}
|
||||||
|
|
||||||
if problems.is_empty() {
|
if problems.is_empty() {
|
||||||
problems.extend(merge(subs, ctx, other_content.clone()));
|
problems.extend(merge(subs, ctx, *other_content));
|
||||||
}
|
}
|
||||||
|
|
||||||
// if problems.is_empty() { problems.extend(unify_pool(subs, pool, real_var, *other_real_var)); }
|
// if problems.is_empty() { problems.extend(unify_pool(subs, pool, real_var, *other_real_var)); }
|
||||||
|
@ -374,7 +374,7 @@ fn unify_structure(
|
||||||
match other {
|
match other {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// If the other is flex, Structure wins!
|
// If the other is flex, Structure wins!
|
||||||
let outcome = merge(subs, ctx, Structure(flat_type.clone()));
|
let outcome = merge(subs, ctx, Structure(*flat_type));
|
||||||
|
|
||||||
// And if we see a flex variable on the right hand side of a presence
|
// And if we see a flex variable on the right hand side of a presence
|
||||||
// constraint, we know we need to open up the structure we're trying to unify with.
|
// constraint, we know we need to open up the structure we're trying to unify with.
|
||||||
|
@ -1158,7 +1158,7 @@ fn unify_flat_type(
|
||||||
use roc_types::subs::FlatType::*;
|
use roc_types::subs::FlatType::*;
|
||||||
|
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())),
|
(EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(*left)),
|
||||||
|
|
||||||
(Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields(subs) => {
|
(Record(fields, ext), EmptyRecord) if fields.has_only_optional_fields(subs) => {
|
||||||
unify_pool(subs, pool, *ext, ctx.second, ctx.mode)
|
unify_pool(subs, pool, *ext, ctx.second, ctx.mode)
|
||||||
|
@ -1172,7 +1172,7 @@ fn unify_flat_type(
|
||||||
unify_record(subs, pool, ctx, *fields1, *ext1, *fields2, *ext2)
|
unify_record(subs, pool, ctx, *fields1, *ext1, *fields2, *ext2)
|
||||||
}
|
}
|
||||||
|
|
||||||
(EmptyTagUnion, EmptyTagUnion) => merge(subs, ctx, Structure(left.clone())),
|
(EmptyTagUnion, EmptyTagUnion) => merge(subs, ctx, Structure(*left)),
|
||||||
|
|
||||||
(TagUnion(tags, ext), EmptyTagUnion) if tags.is_empty() => {
|
(TagUnion(tags, ext), EmptyTagUnion) if tags.is_empty() => {
|
||||||
unify_pool(subs, pool, *ext, ctx.second, ctx.mode)
|
unify_pool(subs, pool, *ext, ctx.second, ctx.mode)
|
||||||
|
@ -1277,7 +1277,7 @@ fn unify_flat_type(
|
||||||
if tag_name_1_ref == tag_name_2_ref {
|
if tag_name_1_ref == tag_name_2_ref {
|
||||||
let problems = unify_pool(subs, pool, *ext1, *ext2, ctx.mode);
|
let problems = unify_pool(subs, pool, *ext1, *ext2, ctx.mode);
|
||||||
if problems.is_empty() {
|
if problems.is_empty() {
|
||||||
let content = subs.get_content_without_compacting(ctx.second).clone();
|
let content = *subs.get_content_without_compacting(ctx.second);
|
||||||
merge(subs, ctx, content)
|
merge(subs, ctx, content)
|
||||||
} else {
|
} else {
|
||||||
problems
|
problems
|
||||||
|
@ -1351,11 +1351,16 @@ fn unify_zip_slices(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content) -> Outcome {
|
fn unify_rigid(
|
||||||
|
subs: &mut Subs,
|
||||||
|
ctx: &Context,
|
||||||
|
name: &SubsIndex<Lowercase>,
|
||||||
|
other: &Content,
|
||||||
|
) -> Outcome {
|
||||||
match other {
|
match other {
|
||||||
FlexVar(_) => {
|
FlexVar(_) => {
|
||||||
// If the other is flex, rigid wins!
|
// If the other is flex, rigid wins!
|
||||||
merge(subs, ctx, RigidVar(name.clone()))
|
merge(subs, ctx, RigidVar(*name))
|
||||||
}
|
}
|
||||||
RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _, _) | RangedNumber(..) => {
|
RigidVar(_) | RecursionVar { .. } | Structure(_) | Alias(_, _, _, _) | RangedNumber(..) => {
|
||||||
if !ctx.mode.contains(Mode::RIGID_AS_FLEX) {
|
if !ctx.mode.contains(Mode::RIGID_AS_FLEX) {
|
||||||
|
@ -1364,7 +1369,7 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content
|
||||||
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
mismatch!("Rigid {:?} with {:?}", ctx.first, &other)
|
||||||
} else {
|
} else {
|
||||||
// We are treating rigid vars as flex vars; admit this
|
// We are treating rigid vars as flex vars; admit this
|
||||||
merge(subs, ctx, other.clone())
|
merge(subs, ctx, *other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Error => {
|
Error => {
|
||||||
|
@ -1378,13 +1383,13 @@ fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &Lowercase, other: &Content
|
||||||
fn unify_flex(
|
fn unify_flex(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
opt_name: &Option<Lowercase>,
|
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||||
other: &Content,
|
other: &Content,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
match other {
|
match other {
|
||||||
FlexVar(None) => {
|
FlexVar(None) => {
|
||||||
// If both are flex, and only left has a name, keep the name around.
|
// If both are flex, and only left has a name, keep the name around.
|
||||||
merge(subs, ctx, FlexVar(opt_name.clone()))
|
merge(subs, ctx, FlexVar(*opt_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexVar(Some(_))
|
FlexVar(Some(_))
|
||||||
|
@ -1396,7 +1401,7 @@ fn unify_flex(
|
||||||
// TODO special-case boolean here
|
// TODO special-case boolean here
|
||||||
// In all other cases, if left is flex, defer to right.
|
// In all other cases, if left is flex, defer to right.
|
||||||
// (This includes using right's name if both are flex and named.)
|
// (This includes using right's name if both are flex and named.)
|
||||||
merge(subs, ctx, other.clone())
|
merge(subs, ctx, *other)
|
||||||
}
|
}
|
||||||
|
|
||||||
Error => merge(subs, ctx, Error),
|
Error => merge(subs, ctx, Error),
|
||||||
|
@ -1408,7 +1413,7 @@ fn unify_recursion(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
pool: &mut Pool,
|
pool: &mut Pool,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
opt_name: &Option<Lowercase>,
|
opt_name: &Option<SubsIndex<Lowercase>>,
|
||||||
structure: Variable,
|
structure: Variable,
|
||||||
other: &Content,
|
other: &Content,
|
||||||
) -> Outcome {
|
) -> Outcome {
|
||||||
|
@ -1419,7 +1424,7 @@ fn unify_recursion(
|
||||||
} => {
|
} => {
|
||||||
// NOTE: structure and other_structure may not be unified yet, but will be
|
// NOTE: structure and other_structure may not be unified yet, but will be
|
||||||
// we should not do that here, it would create an infinite loop!
|
// we should not do that here, it would create an infinite loop!
|
||||||
let name = opt_name.clone().or_else(|| other_opt_name.clone());
|
let name = (*opt_name).or_else(|| *other_opt_name);
|
||||||
merge(
|
merge(
|
||||||
subs,
|
subs,
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -1441,7 +1446,7 @@ fn unify_recursion(
|
||||||
ctx,
|
ctx,
|
||||||
RecursionVar {
|
RecursionVar {
|
||||||
structure,
|
structure,
|
||||||
opt_name: opt_name.clone(),
|
opt_name: *opt_name,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::ui::util::path_to_string;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use cgmath::Vector2;
|
use cgmath::Vector2;
|
||||||
use fs_extra::dir::{copy, ls, CopyOptions, DirEntryAttr, DirEntryValue};
|
use fs_extra::dir::{copy, ls, CopyOptions, DirEntryAttr, DirEntryValue};
|
||||||
|
use futures::TryFutureExt;
|
||||||
use pipelines::RectResources;
|
use pipelines::RectResources;
|
||||||
use roc_ast::lang::env::Env;
|
use roc_ast::lang::env::Env;
|
||||||
use roc_ast::mem_pool::pool::Pool;
|
use roc_ast::mem_pool::pool::Pool;
|
||||||
|
@ -73,28 +74,25 @@ fn run_event_loop(project_dir_path_opt: Option<&Path>) -> Result<(), Box<dyn Err
|
||||||
|
|
||||||
// Initialize GPU
|
// Initialize GPU
|
||||||
let (gpu_device, cmd_queue) = futures::executor::block_on(async {
|
let (gpu_device, cmd_queue) = futures::executor::block_on(async {
|
||||||
let adapter = instance
|
create_device(
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
&instance,
|
||||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
&surface,
|
||||||
compatible_surface: Some(&surface),
|
wgpu::PowerPreference::HighPerformance,
|
||||||
force_fallback_adapter: false,
|
false,
|
||||||
|
)
|
||||||
|
.or_else(|_| create_device(&instance, &surface, wgpu::PowerPreference::LowPower, false))
|
||||||
|
.or_else(|_| {
|
||||||
|
create_device(
|
||||||
|
&instance,
|
||||||
|
&surface,
|
||||||
|
wgpu::PowerPreference::HighPerformance,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!("Failed to request device: `{}`", err);
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect(r#"Request adapter
|
|
||||||
If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor
|
|
||||||
"#);
|
|
||||||
|
|
||||||
adapter
|
|
||||||
.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
label: None,
|
|
||||||
features: wgpu::Features::empty(),
|
|
||||||
limits: wgpu::Limits::default(),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.expect("Request device")
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create staging belt and a local pool
|
// Create staging belt and a local pool
|
||||||
|
@ -395,6 +393,39 @@ fn run_event_loop(project_dir_path_opt: Option<&Path>) -> Result<(), Box<dyn Err
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_device(
|
||||||
|
instance: &wgpu::Instance,
|
||||||
|
surface: &wgpu::Surface,
|
||||||
|
power_preference: wgpu::PowerPreference,
|
||||||
|
force_fallback_adapter: bool,
|
||||||
|
) -> Result<(wgpu::Device, wgpu::Queue), wgpu::RequestDeviceError> {
|
||||||
|
if force_fallback_adapter {
|
||||||
|
log::error!("Falling back to software renderer. GPU acceleration has been disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let adapter = instance
|
||||||
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference,
|
||||||
|
compatible_surface: Some(surface),
|
||||||
|
force_fallback_adapter,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.expect(r#"Request adapter
|
||||||
|
If you're running this from inside nix, follow the instructions here to resolve this: https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#editor
|
||||||
|
"#);
|
||||||
|
|
||||||
|
adapter
|
||||||
|
.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
features: wgpu::Features::empty(),
|
||||||
|
limits: wgpu::Limits::default(),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
fn draw_rects(
|
fn draw_rects(
|
||||||
all_rects: &[Rect],
|
all_rects: &[Rect],
|
||||||
encoder: &mut CommandEncoder,
|
encoder: &mut CommandEncoder,
|
||||||
|
|
|
@ -49,4 +49,3 @@ closure4 = \_ ->
|
||||||
Task.succeed {}
|
Task.succeed {}
|
||||||
|> Task.after (\_ -> Task.succeed x)
|
|> Task.after (\_ -> Task.succeed x)
|
||||||
|> Task.map (\_ -> {})
|
|> Task.map (\_ -> {})
|
||||||
|
|
||||||
|
|
|
@ -73,4 +73,3 @@ swap = \i, j, list ->
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
|
|
@ -112,4 +112,3 @@ balance = \color, key, value, left, right ->
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Node color key value left right
|
Node color key value left right
|
||||||
|
|
||||||
|
|
|
@ -21,4 +21,3 @@ main =
|
||||||
|
|
||||||
Err _ ->
|
Err _ ->
|
||||||
Task.putLine "sadness"
|
Task.putLine "sadness"
|
||||||
|
|
||||||
|
|
9
examples/cli/platform/Cargo.lock
generated
9
examples/cli/platform/Cargo.lock
generated
|
@ -19,3 +19,12 @@ checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f406d6ee68db6796e11ffd7b4d171864c58b7451e79ef9460ea33c287a1f89a7"
|
||||||
|
|
9
examples/false-interpreter/platform/Cargo.lock
generated
9
examples/false-interpreter/platform/Cargo.lock
generated
|
@ -19,3 +19,12 @@ checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f406d6ee68db6796e11ffd7b4d171864c58b7451e79ef9460ea33c287a1f89a7"
|
||||||
|
|
|
@ -79,12 +79,12 @@ pub export fn main() u8 {
|
||||||
|
|
||||||
const result = roc__mainForHost_1_exposed(10);
|
const result = roc__mainForHost_1_exposed(10);
|
||||||
|
|
||||||
stdout.print("{d}\n", .{result}) catch unreachable;
|
|
||||||
|
|
||||||
// end time
|
// end time
|
||||||
var ts2: std.os.timespec = undefined;
|
var ts2: std.os.timespec = undefined;
|
||||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
||||||
|
|
||||||
|
stdout.print("{d}\n", .{result}) catch unreachable;
|
||||||
|
|
||||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||||
|
|
||||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||||
|
|
11
examples/gui/platform/Cargo.lock
generated
11
examples/gui/platform/Cargo.lock
generated
|
@ -2090,6 +2090,9 @@ checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions 0.1.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rodio"
|
name = "rodio"
|
||||||
|
@ -2292,6 +2295,12 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f406d6ee68db6796e11ffd7b4d171864c58b7451e79ef9460ea33c287a1f89a7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2403,7 +2412,7 @@ checksum = "4ee73e6e4924fe940354b8d4d98cad5231175d615cd855b758adc658c0aac6a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"rand",
|
"rand",
|
||||||
"static_assertions",
|
"static_assertions 1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
9
examples/hello-rust/platform/Cargo.lock
generated
9
examples/hello-rust/platform/Cargo.lock
generated
|
@ -19,3 +19,12 @@ checksum = "56d855069fafbb9b344c0f962150cd2c1187975cb1c22c1522c240d8c4986714"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roc_std"
|
name = "roc_std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_assertions"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f406d6ee68db6796e11ffd7b4d171864c58b7451e79ef9460ea33c287a1f89a7"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
To run, go to the project home directory and run:
|
To run, go to the project home directory and run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo run -- build --backend=wasm32 examples/hello-web/Hello.roc
|
$ cargo run -- build --target=wasm32 examples/hello-web/Hello.roc
|
||||||
```
|
```
|
||||||
|
|
||||||
Then `cd` into the example directory and run any web server that can handle WebAssembly.
|
Then `cd` into the example directory and run any web server that can handle WebAssembly.
|
||||||
|
|
|
@ -92,15 +92,15 @@ pub fn main() u8 {
|
||||||
// actually call roc to populate the callresult
|
// actually call roc to populate the callresult
|
||||||
var callresult = roc__mainForHost_1_exposed();
|
var callresult = roc__mainForHost_1_exposed();
|
||||||
|
|
||||||
|
// end time
|
||||||
|
var ts2: std.os.timespec = undefined;
|
||||||
|
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
||||||
|
|
||||||
// stdout the result
|
// stdout the result
|
||||||
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
stdout.print("{s}\n", .{callresult.asSlice()}) catch unreachable;
|
||||||
|
|
||||||
callresult.deinit();
|
callresult.deinit();
|
||||||
|
|
||||||
// end time
|
|
||||||
var ts2: std.os.timespec = undefined;
|
|
||||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
|
||||||
|
|
||||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||||
|
|
||||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||||
|
|
|
@ -112,6 +112,10 @@ pub export fn main() u8 {
|
||||||
const length = std.math.min(20, callresult.length);
|
const length = std.math.min(20, callresult.length);
|
||||||
var result = callresult.elements[0..length];
|
var result = callresult.elements[0..length];
|
||||||
|
|
||||||
|
// end time
|
||||||
|
var ts2: std.os.timespec = undefined;
|
||||||
|
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
||||||
|
|
||||||
for (result) |x, i| {
|
for (result) |x, i| {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
stdout.print("[{}, ", .{x}) catch unreachable;
|
stdout.print("[{}, ", .{x}) catch unreachable;
|
||||||
|
@ -122,10 +126,6 @@ pub export fn main() u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// end time
|
|
||||||
var ts2: std.os.timespec = undefined;
|
|
||||||
std.os.clock_gettime(std.os.CLOCK_REALTIME, &ts2) catch unreachable;
|
|
||||||
|
|
||||||
const delta = to_seconds(ts2) - to_seconds(ts1);
|
const delta = to_seconds(ts2) - to_seconds(ts1);
|
||||||
|
|
||||||
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
stderr.print("runtime: {d:.3}ms\n", .{delta * 1000}) catch unreachable;
|
||||||
|
|
|
@ -18,5 +18,6 @@ roc_mono = {path = "../compiler/mono"}
|
||||||
roc_parse = {path = "../compiler/parse"}
|
roc_parse = {path = "../compiler/parse"}
|
||||||
roc_region = {path = "../compiler/region"}
|
roc_region = {path = "../compiler/region"}
|
||||||
roc_reporting = {path = "../reporting"}
|
roc_reporting = {path = "../reporting"}
|
||||||
|
roc_std = {path = "../roc_std", default-features = false}
|
||||||
roc_target = {path = "../compiler/roc_target"}
|
roc_target = {path = "../compiler/roc_target"}
|
||||||
roc_types = {path = "../compiler/types"}
|
roc_types = {path = "../compiler/types"}
|
||||||
|
|
|
@ -13,6 +13,7 @@ use roc_mono::layout::{
|
||||||
};
|
};
|
||||||
use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral};
|
use roc_parse::ast::{AssignedField, Collection, Expr, StrLiteral};
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
use roc_std::RocDec;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
use roc_types::subs::{Content, FlatType, GetSubsSlice, RecordFields, Subs, UnionTags, Variable};
|
||||||
|
|
||||||
|
@ -279,13 +280,6 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||||
) -> Result<Expr<'a>, ToAstProblem> {
|
) -> Result<Expr<'a>, ToAstProblem> {
|
||||||
let (newtype_containers, content) = unroll_newtypes(env, content);
|
let (newtype_containers, content) = unroll_newtypes(env, content);
|
||||||
let content = unroll_aliases(env, content);
|
let content = unroll_aliases(env, content);
|
||||||
let result = match layout {
|
|
||||||
Layout::Builtin(Builtin::Bool) => Ok(app
|
|
||||||
.call_function(main_fn_name, |mem: &A::Memory, num: bool| {
|
|
||||||
bool_to_ast(env, mem, num, content)
|
|
||||||
})),
|
|
||||||
Layout::Builtin(Builtin::Int(int_width)) => {
|
|
||||||
use IntWidth::*;
|
|
||||||
|
|
||||||
macro_rules! helper {
|
macro_rules! helper {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
|
@ -295,6 +289,14 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result = match layout {
|
||||||
|
Layout::Builtin(Builtin::Bool) => Ok(app
|
||||||
|
.call_function(main_fn_name, |mem: &A::Memory, num: bool| {
|
||||||
|
bool_to_ast(env, mem, num, content)
|
||||||
|
})),
|
||||||
|
Layout::Builtin(Builtin::Int(int_width)) => {
|
||||||
|
use IntWidth::*;
|
||||||
|
|
||||||
let result = match int_width {
|
let result = match int_width {
|
||||||
U8 | I8 => {
|
U8 | I8 => {
|
||||||
// NOTE: `helper!` does not handle 8-bit numbers yet
|
// NOTE: `helper!` does not handle 8-bit numbers yet
|
||||||
|
@ -317,14 +319,6 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||||
Layout::Builtin(Builtin::Float(float_width)) => {
|
Layout::Builtin(Builtin::Float(float_width)) => {
|
||||||
use FloatWidth::*;
|
use FloatWidth::*;
|
||||||
|
|
||||||
macro_rules! helper {
|
|
||||||
($ty:ty) => {
|
|
||||||
app.call_function(main_fn_name, |_, num: $ty| {
|
|
||||||
num_to_ast(env, number_literal_to_ast(env.arena, num), content)
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = match float_width {
|
let result = match float_width {
|
||||||
F32 => helper!(f32),
|
F32 => helper!(f32),
|
||||||
F64 => helper!(f64),
|
F64 => helper!(f64),
|
||||||
|
@ -333,6 +327,7 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
Layout::Builtin(Builtin::Decimal) => Ok(helper!(RocDec)),
|
||||||
Layout::Builtin(Builtin::Str) => {
|
Layout::Builtin(Builtin::Str) => {
|
||||||
let size = layout.stack_size(env.target_info) as usize;
|
let size = layout.stack_size(env.target_info) as usize;
|
||||||
Ok(
|
Ok(
|
||||||
|
@ -1246,5 +1241,9 @@ fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> E
|
||||||
/// This is centralized in case we want to format it differently later,
|
/// This is centralized in case we want to format it differently later,
|
||||||
/// e.g. adding underscores for large numbers
|
/// e.g. adding underscores for large numbers
|
||||||
fn number_literal_to_ast<T: std::fmt::Display>(arena: &Bump, num: T) -> Expr<'_> {
|
fn number_literal_to_ast<T: std::fmt::Display>(arena: &Bump, num: T) -> Expr<'_> {
|
||||||
Expr::Num(arena.alloc(format!("{}", num)))
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
let mut string = bumpalo::collections::String::with_capacity_in(64, arena);
|
||||||
|
write!(string, "{}", num).unwrap();
|
||||||
|
Expr::Num(string.into_bump_str())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use roc_parse::ast::Expr;
|
use roc_parse::ast::Expr;
|
||||||
|
use roc_std::RocDec;
|
||||||
|
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod gen;
|
pub mod gen;
|
||||||
|
@ -47,5 +48,10 @@ pub trait ReplAppMemory {
|
||||||
fn deref_f32(&self, addr: usize) -> f32;
|
fn deref_f32(&self, addr: usize) -> f32;
|
||||||
fn deref_f64(&self, addr: usize) -> f64;
|
fn deref_f64(&self, addr: usize) -> f64;
|
||||||
|
|
||||||
|
fn deref_dec(&self, addr: usize) -> RocDec {
|
||||||
|
let bits = self.deref_i128(addr);
|
||||||
|
RocDec(bits)
|
||||||
|
}
|
||||||
|
|
||||||
fn deref_str(&self, addr: usize) -> &str;
|
fn deref_str(&self, addr: usize) -> &str;
|
||||||
}
|
}
|
||||||
|
|
|
@ -994,6 +994,7 @@ fn issue_2588_record_with_function_and_nonfunction() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn opaque_apply() {
|
fn opaque_apply() {
|
||||||
expect_success(
|
expect_success(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
@ -1036,3 +1037,17 @@ fn opaque_pattern_and_call() {
|
||||||
r#"Package {} A : F {} [ A ]*"#,
|
r#"Package {} A : F {} [ A ]*"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dec_in_repl() {
|
||||||
|
expect_success(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x: Dec
|
||||||
|
x=1.23
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
r#"1.23 : Dec"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -43,16 +43,20 @@ section.history {
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
#history-text .input {
|
.history-item {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.history-item .input {
|
||||||
|
margin: 0;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
#history-text .output {
|
.history-item .output {
|
||||||
margin-bottom: 16px;
|
margin: 0;
|
||||||
}
|
}
|
||||||
#history-text .output-ok {
|
.history-item .output-ok {
|
||||||
color: #0f8;
|
color: #0f8;
|
||||||
}
|
}
|
||||||
#history-text .output-error {
|
.history-item .output-error {
|
||||||
color: #f00;
|
color: #f00;
|
||||||
}
|
}
|
||||||
.code {
|
.code {
|
||||||
|
@ -64,9 +68,7 @@ section.source {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
section.source textarea {
|
section.source textarea {
|
||||||
height: 32px;
|
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,9 @@ function onInputKeyup(event) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ENTER:
|
case ENTER:
|
||||||
|
if (!event.shiftKey) {
|
||||||
onInputChange({ target: repl.elemSourceInput });
|
onInputChange({ target: repl.elemSourceInput });
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -168,12 +170,13 @@ function createHistoryEntry(inputText) {
|
||||||
const historyIndex = repl.inputHistory.length;
|
const historyIndex = repl.inputHistory.length;
|
||||||
repl.inputHistory.push(inputText);
|
repl.inputHistory.push(inputText);
|
||||||
|
|
||||||
const inputElem = document.createElement("div");
|
const inputElem = document.createElement("pre");
|
||||||
inputElem.textContent = "> " + inputText;
|
inputElem.textContent = inputText;
|
||||||
inputElem.classList.add("input");
|
inputElem.classList.add("input");
|
||||||
|
|
||||||
const historyItem = document.createElement("div");
|
const historyItem = document.createElement("div");
|
||||||
historyItem.appendChild(inputElem);
|
historyItem.appendChild(inputElem);
|
||||||
|
historyItem.classList.add("history-item");
|
||||||
|
|
||||||
repl.elemHistory.appendChild(historyItem);
|
repl.elemHistory.appendChild(historyItem);
|
||||||
repl.elemHistory.scrollTop = repl.elemHistory.scrollHeight;
|
repl.elemHistory.scrollTop = repl.elemHistory.scrollHeight;
|
||||||
|
@ -182,7 +185,7 @@ function createHistoryEntry(inputText) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateHistoryEntry(index, ok, outputText) {
|
function updateHistoryEntry(index, ok, outputText) {
|
||||||
const outputElem = document.createElement("div");
|
const outputElem = document.createElement("pre");
|
||||||
outputElem.textContent = outputText;
|
outputElem.textContent = outputText;
|
||||||
outputElem.classList.add("output");
|
outputElem.classList.add("output");
|
||||||
outputElem.classList.add(ok ? "output-ok" : "output-error");
|
outputElem.classList.add(ok ? "output-ok" : "output-error");
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Roc REPL</title>
|
<title>Roc REPL</title>
|
||||||
<link rel="stylesheet" href="/repl.css" />
|
<link rel="stylesheet" href="/repl.css" />
|
||||||
|
@ -20,10 +19,15 @@
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="source">
|
<section class="source">
|
||||||
<textarea autofocus id="source-input" class="code" placeholder="Type some Roc code and press Enter"></textarea>
|
<textarea
|
||||||
|
rows="5"
|
||||||
|
autofocus
|
||||||
|
id="source-input"
|
||||||
|
class="code"
|
||||||
|
placeholder="Type some Roc code and press Enter. (Use Shift+Enter for multi-line input)"
|
||||||
|
></textarea>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<script type="module" src="/repl.js"></script>
|
<script type="module" src="/repl.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use roc_collections::all::MutSet;
|
use roc_collections::all::MutSet;
|
||||||
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
use roc_problem::can::PrecedenceProblem::BothNonAssociative;
|
||||||
use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, RuntimeError};
|
use roc_problem::can::{
|
||||||
|
BadPattern, ExtensionTypeKind, FloatErrorKind, IntErrorKind, Problem, RuntimeError,
|
||||||
|
};
|
||||||
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
use roc_region::all::{LineColumn, LineColumnRegion, LineInfo, Loc, Region};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
@ -33,6 +35,7 @@ const OPAQUE_NOT_DEFINED: &str = "OPAQUE TYPE NOT DEFINED";
|
||||||
const OPAQUE_DECLARED_OUTSIDE_SCOPE: &str = "OPAQUE TYPE DECLARED OUTSIDE SCOPE";
|
const OPAQUE_DECLARED_OUTSIDE_SCOPE: &str = "OPAQUE TYPE DECLARED OUTSIDE SCOPE";
|
||||||
const OPAQUE_NOT_APPLIED: &str = "OPAQUE TYPE NOT APPLIED";
|
const OPAQUE_NOT_APPLIED: &str = "OPAQUE TYPE NOT APPLIED";
|
||||||
const OPAQUE_OVER_APPLIED: &str = "OPAQUE TYPE APPLIED TO TOO MANY ARGS";
|
const OPAQUE_OVER_APPLIED: &str = "OPAQUE TYPE APPLIED TO TOO MANY ARGS";
|
||||||
|
const INVALID_EXTENSION_TYPE: &str = "INVALID_EXTENSION_TYPE";
|
||||||
|
|
||||||
pub fn can_problem<'b>(
|
pub fn can_problem<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
|
@ -492,6 +495,34 @@ pub fn can_problem<'b>(
|
||||||
title = NESTED_DATATYPE.to_string();
|
title = NESTED_DATATYPE.to_string();
|
||||||
severity = Severity::RuntimeError;
|
severity = Severity::RuntimeError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Problem::InvalidExtensionType { region, kind } => {
|
||||||
|
let (kind_str, can_only_contain) = match kind {
|
||||||
|
ExtensionTypeKind::Record => ("record", "a type variable or another record"),
|
||||||
|
ExtensionTypeKind::TagUnion => {
|
||||||
|
("tag union", "a type variable or another tag union")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("This "),
|
||||||
|
alloc.text(kind_str),
|
||||||
|
alloc.reflow(" extension type is invalid:"),
|
||||||
|
]),
|
||||||
|
alloc.region(lines.convert_region(region)),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.note("A "),
|
||||||
|
alloc.reflow(kind_str),
|
||||||
|
alloc.reflow(" extension variable can only contain "),
|
||||||
|
alloc.reflow(can_only_contain),
|
||||||
|
alloc.reflow("."),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
title = INVALID_EXTENSION_TYPE.to_string();
|
||||||
|
severity = Severity::RuntimeError;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Report {
|
Report {
|
||||||
|
|
|
@ -136,6 +136,8 @@ fn pattern_to_doc<'b>(
|
||||||
pattern_to_doc_help(alloc, pattern, false)
|
pattern_to_doc_help(alloc, pattern, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AFTER_TAG_INDENT: &str = " ";
|
||||||
|
|
||||||
fn pattern_to_doc_help<'b>(
|
fn pattern_to_doc_help<'b>(
|
||||||
alloc: &'b RocDocAllocator<'b>,
|
alloc: &'b RocDocAllocator<'b>,
|
||||||
pattern: roc_exhaustive::Pattern,
|
pattern: roc_exhaustive::Pattern,
|
||||||
|
@ -160,7 +162,22 @@ fn pattern_to_doc_help<'b>(
|
||||||
},
|
},
|
||||||
Ctor(union, tag_id, args) => {
|
Ctor(union, tag_id, args) => {
|
||||||
match union.render_as {
|
match union.render_as {
|
||||||
RenderAs::Guard => panic!("can this happen? inform Folkert"),
|
RenderAs::Guard => {
|
||||||
|
// #Guard <fake-condition-tag> <unexhausted-pattern>
|
||||||
|
debug_assert_eq!(
|
||||||
|
union.alternatives[tag_id.0 as usize].name,
|
||||||
|
TagName::Global("#Guard".into())
|
||||||
|
);
|
||||||
|
debug_assert!(args.len() == 2);
|
||||||
|
let tag = pattern_to_doc_help(alloc, args[1].clone(), in_type_param);
|
||||||
|
alloc.concat(vec![
|
||||||
|
tag,
|
||||||
|
alloc.text(AFTER_TAG_INDENT),
|
||||||
|
alloc.text("(note the lack of an "),
|
||||||
|
alloc.keyword("if"),
|
||||||
|
alloc.text(" clause)"),
|
||||||
|
])
|
||||||
|
}
|
||||||
RenderAs::Record(field_names) => {
|
RenderAs::Record(field_names) => {
|
||||||
let mut arg_docs = Vec::with_capacity(args.len());
|
let mut arg_docs = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use roc_can::expected::{Expected, PExpected};
|
use roc_can::expected::{Expected, PExpected};
|
||||||
use roc_collections::all::{Index, MutSet, SendMap};
|
use roc_collections::all::{HumanIndex, MutSet, SendMap};
|
||||||
use roc_module::called_via::{BinOp, CalledVia};
|
use roc_module::called_via::{BinOp, CalledVia};
|
||||||
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
|
use roc_module::ident::{Ident, IdentStr, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -350,7 +350,7 @@ fn to_expr_report<'b>(
|
||||||
num_branches,
|
num_branches,
|
||||||
..
|
..
|
||||||
} if num_branches == 2 => alloc.concat(vec![
|
} if num_branches == 2 => alloc.concat(vec![
|
||||||
alloc.keyword(if index == Index::FIRST {
|
alloc.keyword(if index == HumanIndex::FIRST {
|
||||||
"then"
|
"then"
|
||||||
} else {
|
} else {
|
||||||
"else"
|
"else"
|
||||||
|
@ -1372,7 +1372,7 @@ fn to_pattern_report<'b>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PReason::WhenMatch { index } => {
|
PReason::WhenMatch { index } => {
|
||||||
if index == Index::FIRST {
|
if index == HumanIndex::FIRST {
|
||||||
let doc = alloc.stack(vec![
|
let doc = alloc.stack(vec![
|
||||||
alloc
|
alloc
|
||||||
.text("The 1st pattern in this ")
|
.text("The 1st pattern in this ")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
extern crate bumpalo;
|
extern crate bumpalo;
|
||||||
|
|
||||||
use self::bumpalo::Bump;
|
use self::bumpalo::Bump;
|
||||||
use roc_can::constraint::Constraint;
|
use roc_can::constraint::{Constraint, Constraints};
|
||||||
use roc_can::env::Env;
|
use roc_can::env::Env;
|
||||||
use roc_can::expected::Expected;
|
use roc_can::expected::Expected;
|
||||||
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
use roc_can::expr::{canonicalize_expr, Expr, Output};
|
||||||
|
@ -28,16 +28,14 @@ pub fn test_home() -> ModuleId {
|
||||||
pub fn infer_expr(
|
pub fn infer_expr(
|
||||||
subs: Subs,
|
subs: Subs,
|
||||||
problems: &mut Vec<solve::TypeError>,
|
problems: &mut Vec<solve::TypeError>,
|
||||||
|
constraints: &Constraints,
|
||||||
constraint: &Constraint,
|
constraint: &Constraint,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
) -> (Content, Subs) {
|
) -> (Content, Subs) {
|
||||||
let env = solve::Env::default();
|
let env = solve::Env::default();
|
||||||
let (solved, _) = solve::run(&env, problems, subs, constraint);
|
let (solved, _) = solve::run(constraints, &env, problems, subs, constraint);
|
||||||
|
|
||||||
let content = solved
|
let content = *solved.inner().get_content_without_compacting(expr_var);
|
||||||
.inner()
|
|
||||||
.get_content_without_compacting(expr_var)
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
(content, solved.into_inner())
|
(content, solved.into_inner())
|
||||||
}
|
}
|
||||||
|
@ -96,6 +94,7 @@ pub struct CanExprOut {
|
||||||
pub var_store: VarStore,
|
pub var_store: VarStore,
|
||||||
pub var: Variable,
|
pub var: Variable,
|
||||||
pub constraint: Constraint,
|
pub constraint: Constraint,
|
||||||
|
pub constraints: Constraints,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -152,9 +151,11 @@ pub fn can_expr_with<'a>(
|
||||||
&loc_expr.value,
|
&loc_expr.value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut constraints = Constraints::new();
|
||||||
let constraint = constrain_expr(
|
let constraint = constrain_expr(
|
||||||
|
&mut constraints,
|
||||||
&roc_constrain::expr::Env {
|
&roc_constrain::expr::Env {
|
||||||
rigids: ImMap::default(),
|
rigids: MutMap::default(),
|
||||||
home,
|
home,
|
||||||
},
|
},
|
||||||
loc_expr.region,
|
loc_expr.region,
|
||||||
|
@ -174,7 +175,7 @@ pub fn can_expr_with<'a>(
|
||||||
|
|
||||||
//load builtin values
|
//load builtin values
|
||||||
let (_introduced_rigids, constraint) =
|
let (_introduced_rigids, constraint) =
|
||||||
constrain_imported_values(imports, constraint, &mut var_store);
|
constrain_imported_values(&mut constraints, imports, constraint, &mut var_store);
|
||||||
|
|
||||||
let mut all_ident_ids = MutMap::default();
|
let mut all_ident_ids = MutMap::default();
|
||||||
|
|
||||||
|
@ -200,6 +201,7 @@ pub fn can_expr_with<'a>(
|
||||||
interns,
|
interns,
|
||||||
var,
|
var,
|
||||||
constraint,
|
constraint,
|
||||||
|
constraints,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ mod test_reporting {
|
||||||
output,
|
output,
|
||||||
var_store,
|
var_store,
|
||||||
var,
|
var,
|
||||||
|
constraints,
|
||||||
constraint,
|
constraint,
|
||||||
home,
|
home,
|
||||||
interns,
|
interns,
|
||||||
|
@ -79,7 +80,8 @@ mod test_reporting {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut unify_problems = Vec::new();
|
let mut unify_problems = Vec::new();
|
||||||
let (_content, mut subs) = infer_expr(subs, &mut unify_problems, &constraint, var);
|
let (_content, mut subs) =
|
||||||
|
infer_expr(subs, &mut unify_problems, &constraints, &constraint, var);
|
||||||
|
|
||||||
name_all_type_vars(var, &mut subs);
|
name_all_type_vars(var, &mut subs);
|
||||||
|
|
||||||
|
@ -8440,4 +8442,127 @@ I need all branches in an `if` to have the same type!
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_polymorphism_with_scoped_type_variables() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : a -> a
|
||||||
|
f = \x ->
|
||||||
|
y : a -> a
|
||||||
|
y = \z -> z
|
||||||
|
|
||||||
|
n = y 1u8
|
||||||
|
x1 = y x
|
||||||
|
(\_ -> x1) n
|
||||||
|
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── TYPE MISMATCH ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
The 1st argument to `y` is not what I expect:
|
||||||
|
|
||||||
|
6│ n = y 1u8
|
||||||
|
^^^
|
||||||
|
|
||||||
|
This argument is an integer of type:
|
||||||
|
|
||||||
|
U8
|
||||||
|
|
||||||
|
But `y` needs the 1st argument to be:
|
||||||
|
|
||||||
|
a
|
||||||
|
|
||||||
|
Tip: The type annotation uses the type variable `a` to say that this
|
||||||
|
definition can produce any type of value. But in the body I see that
|
||||||
|
it will only produce a `U8` value of a single specific type. Maybe
|
||||||
|
change the type annotation to be more specific? Maybe change the code
|
||||||
|
to be more general?
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_exhaustive_with_guard() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x : [A]
|
||||||
|
when x is
|
||||||
|
A if True -> ""
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNSAFE PATTERN ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This `when` does not cover all the possibilities:
|
||||||
|
|
||||||
|
2│> when x is
|
||||||
|
3│> A if True -> ""
|
||||||
|
|
||||||
|
Other possibilities include:
|
||||||
|
|
||||||
|
A (note the lack of an if clause)
|
||||||
|
|
||||||
|
I would have to crash if I saw one of those! Add branches for them!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_record_extension_type() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : { x : Nat }U32
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── INVALID_EXTENSION_TYPE ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This record extension type is invalid:
|
||||||
|
|
||||||
|
1│ f : { x : Nat }U32
|
||||||
|
^^^
|
||||||
|
|
||||||
|
Note: A record extension variable can only contain a type variable or
|
||||||
|
another record.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_tag_extension_type() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : [ A ]Str
|
||||||
|
f
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── INVALID_EXTENSION_TYPE ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
This tag union extension type is invalid:
|
||||||
|
|
||||||
|
1│ f : [ A ]Str
|
||||||
|
^^^
|
||||||
|
|
||||||
|
Note: A tag union extension variable can only contain a type variable
|
||||||
|
or another tag union.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ readme = "README.md"
|
||||||
repository = "https://github.com/rtfeldman/roc"
|
repository = "https://github.com/rtfeldman/roc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
static_assertions = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
indoc = "1.0.3"
|
indoc = "1.0.3"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#![crate_type = "lib"]
|
#![crate_type = "lib"]
|
||||||
#![no_std]
|
// #![no_std]
|
||||||
use core::ffi::c_void;
|
use core::ffi::c_void;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||||
use core::ops::Drop;
|
use core::ops::Drop;
|
||||||
|
use core::str;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
mod rc;
|
mod rc;
|
||||||
mod roc_list;
|
mod roc_list;
|
||||||
|
@ -214,9 +216,10 @@ impl RocDec {
|
||||||
pub const MIN: Self = Self(i128::MIN);
|
pub const MIN: Self = Self(i128::MIN);
|
||||||
pub const MAX: Self = Self(i128::MAX);
|
pub const MAX: Self = Self(i128::MAX);
|
||||||
|
|
||||||
pub const DECIMAL_PLACES: u32 = 18;
|
const DECIMAL_PLACES: usize = 18;
|
||||||
|
const ONE_POINT_ZERO: i128 = 10i128.pow(Self::DECIMAL_PLACES as u32);
|
||||||
pub const ONE_POINT_ZERO: i128 = 10i128.pow(Self::DECIMAL_PLACES);
|
const MAX_DIGITS: usize = 39;
|
||||||
|
const MAX_STR_LENGTH: usize = Self::MAX_DIGITS + 2; // + 2 here to account for the sign & decimal dot
|
||||||
|
|
||||||
#[allow(clippy::should_implement_trait)]
|
#[allow(clippy::should_implement_trait)]
|
||||||
pub fn from_str(value: &str) -> Option<Self> {
|
pub fn from_str(value: &str) -> Option<Self> {
|
||||||
|
@ -231,7 +234,7 @@ impl RocDec {
|
||||||
};
|
};
|
||||||
|
|
||||||
let opt_after_point = match parts.next() {
|
let opt_after_point = match parts.next() {
|
||||||
Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => Some(answer),
|
Some(answer) if answer.len() <= Self::DECIMAL_PLACES => Some(answer),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -247,7 +250,7 @@ impl RocDec {
|
||||||
Ok(answer) => {
|
Ok(answer) => {
|
||||||
// Translate e.g. the 1 from 0.1 into 10000000000000000000
|
// Translate e.g. the 1 from 0.1 into 10000000000000000000
|
||||||
// by "restoring" the elided trailing zeroes to the number!
|
// by "restoring" the elided trailing zeroes to the number!
|
||||||
let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len();
|
let trailing_zeroes = Self::DECIMAL_PLACES - after_point.len();
|
||||||
let lo = answer * 10i128.pow(trailing_zeroes as u32);
|
let lo = answer * 10i128.pow(trailing_zeroes as u32);
|
||||||
|
|
||||||
if !before_point.starts_with('-') {
|
if !before_point.starts_with('-') {
|
||||||
|
@ -266,7 +269,7 @@ impl RocDec {
|
||||||
|
|
||||||
// Calculate the high digits - the ones before the decimal point.
|
// Calculate the high digits - the ones before the decimal point.
|
||||||
match before_point.parse::<i128>() {
|
match before_point.parse::<i128>() {
|
||||||
Ok(answer) => match answer.checked_mul(10i128.pow(Self::DECIMAL_PLACES)) {
|
Ok(answer) => match answer.checked_mul(Self::ONE_POINT_ZERO) {
|
||||||
Some(hi) => hi.checked_add(lo).map(Self),
|
Some(hi) => hi.checked_add(lo).map(Self),
|
||||||
None => None,
|
None => None,
|
||||||
},
|
},
|
||||||
|
@ -277,4 +280,73 @@ impl RocDec {
|
||||||
pub fn from_str_to_i128_unsafe(val: &str) -> i128 {
|
pub fn from_str_to_i128_unsafe(val: &str) -> i128 {
|
||||||
Self::from_str(val).unwrap().0
|
Self::from_str(val).unwrap().0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_str_helper(&self, bytes: &mut [u8; Self::MAX_STR_LENGTH]) -> usize {
|
||||||
|
if self.0 == 0 {
|
||||||
|
write!(&mut bytes[..], "{}", "0").unwrap();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_negative = (self.0 < 0) as usize;
|
||||||
|
|
||||||
|
static_assertions::const_assert!(Self::DECIMAL_PLACES + 1 == 19);
|
||||||
|
// The :019 in the following write! is computed as Self::DECIMAL_PLACES + 1. If you change
|
||||||
|
// Self::DECIMAL_PLACES, this assert should remind you to change that format string as
|
||||||
|
// well.
|
||||||
|
//
|
||||||
|
// By using the :019 format, we're guaranteeing that numbers less than 1, say 0.01234
|
||||||
|
// get their leading zeros placed in bytes for us. i.e. bytes = b"0012340000000000000"
|
||||||
|
write!(&mut bytes[..], "{:019}", self.0).unwrap();
|
||||||
|
|
||||||
|
// If self represents 1234.5678, then bytes is b"1234567800000000000000".
|
||||||
|
let mut i = Self::MAX_STR_LENGTH - 1;
|
||||||
|
// Find the last place where we have actual data.
|
||||||
|
while bytes[i] == 0 {
|
||||||
|
i = i - 1;
|
||||||
|
}
|
||||||
|
// At this point i is 21 because bytes[21] is the final '0' in b"1234567800000000000000".
|
||||||
|
|
||||||
|
let decimal_location = i - Self::DECIMAL_PLACES + 1 + is_negative;
|
||||||
|
// decimal_location = 4
|
||||||
|
|
||||||
|
while bytes[i] == ('0' as u8) && i >= decimal_location {
|
||||||
|
bytes[i] = 0;
|
||||||
|
i = i - 1;
|
||||||
|
}
|
||||||
|
// Now i = 7, because bytes[7] = '8', and bytes = b"12345678"
|
||||||
|
|
||||||
|
if i < decimal_location {
|
||||||
|
// This means that we've removed trailing zeros and are left with an integer. Our
|
||||||
|
// convention is to print these without a decimal point or trailing zeros, so we're done.
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = i + 1;
|
||||||
|
while i >= decimal_location {
|
||||||
|
bytes[i + 1] = bytes[i];
|
||||||
|
i = i - 1;
|
||||||
|
}
|
||||||
|
bytes[i + 1] = bytes[i];
|
||||||
|
// Now i = 4, and bytes = b"123455678"
|
||||||
|
|
||||||
|
bytes[decimal_location] = '.' as u8;
|
||||||
|
// Finally bytes = b"1234.5678"
|
||||||
|
|
||||||
|
ret + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_str(&self) -> RocStr {
|
||||||
|
let mut bytes = [0 as u8; Self::MAX_STR_LENGTH];
|
||||||
|
let last_idx = self.to_str_helper(&mut bytes);
|
||||||
|
unsafe { RocStr::from_slice(&bytes[0..last_idx]) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RocDec {
|
||||||
|
fn fmt(&self, fmtr: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let mut bytes = [0 as u8; Self::MAX_STR_LENGTH];
|
||||||
|
let last_idx = self.to_str_helper(&mut bytes);
|
||||||
|
let result = unsafe { str::from_utf8_unchecked(&bytes[0..last_idx]) };
|
||||||
|
write!(fmtr, "{}", result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
3
rust-toolchain.toml
Normal file
3
rust-toolchain.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.58.0" # make sure to update the rust version in Earthfile as well
|
||||||
|
profile = "default"
|
|
@ -66,10 +66,7 @@ let
|
||||||
# tools for development environment
|
# tools for development environment
|
||||||
less
|
less
|
||||||
]) ++ (with unstable-pkgs; [
|
]) ++ (with unstable-pkgs; [
|
||||||
rustc
|
rustup
|
||||||
cargo
|
|
||||||
clippy
|
|
||||||
rustfmt
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
in pkgs.mkShell {
|
in pkgs.mkShell {
|
||||||
|
|
2
vendor/ena/src/unify/mod.rs
vendored
2
vendor/ena/src/unify/mod.rs
vendored
|
@ -470,7 +470,7 @@ where
|
||||||
K1: Into<K>,
|
K1: Into<K>,
|
||||||
{
|
{
|
||||||
let id = id.into();
|
let id = id.into();
|
||||||
let id = self.get_root_key_without_compacting(id);
|
let id = self.get_root_key(id);
|
||||||
self.value_mut(id)
|
self.value_mut(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
www/build.sh
11
www/build.sh
|
@ -12,13 +12,18 @@ rm -rf build/
|
||||||
cp -r public/ build/
|
cp -r public/ build/
|
||||||
|
|
||||||
pushd build
|
pushd build
|
||||||
|
|
||||||
# grab the source code and copy it to Netlify's server; if it's not there, fail the build.
|
# grab the source code and copy it to Netlify's server; if it's not there, fail the build.
|
||||||
wget https://github.com/rtfeldman/elm-css/files/8037422/roc-source-code.zip
|
wget https://github.com/rtfeldman/elm-css/files/8037422/roc-source-code.zip
|
||||||
|
|
||||||
# grab the pre-compiled REPL and copy it to Netlify's server; if it's not there, fail the build.
|
# grab the pre-compiled REPL and copy it to Netlify's server; if it's not there, fail the build.
|
||||||
wget https://github.com/brian-carroll/mock-repl/files/8167902/roc_repl_wasm.tar.gz
|
wget https://github.com/brian-carroll/mock-repl/archive/refs/heads/deploy.zip
|
||||||
tar xzvf roc_repl_wasm.tar.gz
|
unzip deploy.zip
|
||||||
rm roc_repl_wasm.tar.gz
|
mv mock-repl-deploy/* .
|
||||||
|
rmdir mock-repl-deploy
|
||||||
|
rm deploy.zip
|
||||||
|
|
||||||
|
# Copy REPL webpage source files
|
||||||
cp -r ../../repl_www/public/* .
|
cp -r ../../repl_www/public/* .
|
||||||
|
|
||||||
popd
|
popd
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue