mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Merge branch 'main' of github.com:roc-lang/roc into wasm_interp_test_gen
This commit is contained in:
commit
d389601035
9
.github/workflows/nightly_macos_x86_64.yml
vendored
9
.github/workflows/nightly_macos_x86_64.yml
vendored
@ -14,16 +14,15 @@ jobs:
|
||||
|
||||
- name: write version to file
|
||||
run: ./ci/write_version.sh
|
||||
|
||||
# build has to be done before tests #2572
|
||||
- name: build release
|
||||
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
|
||||
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
|
||||
|
||||
- name: execute rust tests
|
||||
run: cargo test --release --locked -- --skip opaque_wrap_function --skip bool_list_literal --skip platform_switching_swift --skip swift_ui
|
||||
# swift tests are skipped because of "Could not find or use auto-linked library 'swiftCompatibilityConcurrency'" on macos-11 x86_64 CI machine
|
||||
# this issue may be caused by using older versions of XCode
|
||||
|
||||
- name: build release
|
||||
run: RUSTFLAGS="-C target-cpu=x86-64" cargo build --features with_sound --release --locked
|
||||
# target-cpu=x86-64 -> For maximal compatibility for all CPU's. Note that this setting will likely make the compiler slower.
|
||||
|
||||
- name: get commit SHA
|
||||
run: echo "SHA=$(git rev-parse --short "$GITHUB_SHA")" >> $GITHUB_ENV
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3700,6 +3700,7 @@ dependencies = [
|
||||
"roc_serialize",
|
||||
"roc_types",
|
||||
"static_assertions",
|
||||
"ven_pretty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -39,6 +39,8 @@ target-all = [
|
||||
"target-wasm32"
|
||||
]
|
||||
|
||||
sanitizers = ["roc_build/sanitizers"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
roc_collections = { path = "../compiler/collections" }
|
||||
|
@ -186,7 +186,9 @@ pub fn build_file<'a>(
|
||||
};
|
||||
|
||||
// We don't need to spawn a rebuild thread when using a prebuilt host.
|
||||
let rebuild_thread = if is_prebuilt {
|
||||
let rebuild_thread = if matches!(link_type, LinkType::Dylib | LinkType::None) {
|
||||
None
|
||||
} else if is_prebuilt {
|
||||
if !preprocessed_host_path.exists() {
|
||||
if prebuilt_requested {
|
||||
eprintln!(
|
||||
@ -378,10 +380,11 @@ pub fn build_file<'a>(
|
||||
|
||||
std::fs::write(app_o_file, &*roc_app_bytes).unwrap();
|
||||
|
||||
let mut inputs = vec![
|
||||
host_input_path.as_path().to_str().unwrap(),
|
||||
app_o_file.to_str().unwrap(),
|
||||
];
|
||||
let mut inputs = vec![app_o_file.to_str().unwrap()];
|
||||
|
||||
if !matches!(link_type, LinkType::Dylib | LinkType::None) {
|
||||
inputs.push(host_input_path.as_path().to_str().unwrap());
|
||||
}
|
||||
|
||||
let builtins_host_tempfile = {
|
||||
#[cfg(unix)]
|
||||
|
@ -568,7 +568,7 @@ mod cli_run {
|
||||
r#"
|
||||
This expectation failed:
|
||||
|
||||
14│ expect x != x
|
||||
18│ expect x != x
|
||||
^^^^^^
|
||||
|
||||
When it failed, these variables had these values:
|
||||
@ -576,8 +576,11 @@ mod cli_run {
|
||||
x : Num *
|
||||
x = 42
|
||||
|
||||
[<ignored for tests> 15:9] 42
|
||||
[<ignored for tests> 16:9] "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||
[<ignored for tests> 19:9] 42
|
||||
[<ignored for tests> 20:9] "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||
[<ignored for tests> 13:9] "abc"
|
||||
[<ignored for tests> 13:9] 10
|
||||
[<ignored for tests> 13:9] A (B C)
|
||||
Program finished!
|
||||
"#
|
||||
),
|
||||
|
1
crates/cli_testing_examples/.gitignore
vendored
1
crates/cli_testing_examples/.gitignore
vendored
@ -4,3 +4,4 @@ libapp.so
|
||||
dynhost
|
||||
preprocessedhost
|
||||
metadata
|
||||
expects
|
||||
|
@ -9,9 +9,17 @@ expect
|
||||
|
||||
a == b
|
||||
|
||||
polyDbg = \x ->
|
||||
dbg x
|
||||
x
|
||||
|
||||
main =
|
||||
x = 42
|
||||
expect x != x
|
||||
dbg x
|
||||
dbg "Fjoer en ferdjer frieten oan dyn geve lea"
|
||||
"Program finished!\n"
|
||||
|
||||
r = {x : polyDbg "abc", y: polyDbg 10u8, z : polyDbg (A (B C))}
|
||||
|
||||
when r is
|
||||
_ -> "Program finished!\n"
|
||||
|
@ -1287,6 +1287,14 @@ fn lowlevel_spec<'a>(
|
||||
|
||||
builder.add_make_tuple(block, &[byte_index, string, is_ok, problem_code])
|
||||
}
|
||||
Dbg => {
|
||||
let arguments = [env.symbols[&arguments[0]]];
|
||||
|
||||
let result_type =
|
||||
layout_spec(env, builder, interner, layout, &WhenRecursive::Unreachable)?;
|
||||
|
||||
builder.add_unknown_with(block, &arguments, result_type)
|
||||
}
|
||||
_other => {
|
||||
// println!("missing {:?}", _other);
|
||||
// TODO overly pessimstic
|
||||
|
@ -47,3 +47,7 @@ target-aarch64 = ["roc_gen_dev/target-aarch64"]
|
||||
target-x86 = []
|
||||
target-x86_64 = ["roc_gen_dev/target-x86_64"]
|
||||
target-wasm32 = []
|
||||
|
||||
# This is used to enable fuzzing and sanitizers.
|
||||
# Example use is describe here: https://github.com/bhansconnect/roc-fuzz
|
||||
sanitizers = []
|
||||
|
@ -1248,14 +1248,17 @@ fn link_macos(
|
||||
input_paths: &[&str],
|
||||
link_type: LinkType,
|
||||
) -> io::Result<(Child, PathBuf)> {
|
||||
let (link_type_arg, output_path) = match link_type {
|
||||
LinkType::Executable => ("-execute", output_path),
|
||||
let (link_type_args, output_path) = match link_type {
|
||||
LinkType::Executable => (vec!["-execute"], output_path),
|
||||
LinkType::Dylib => {
|
||||
let mut output_path = output_path;
|
||||
|
||||
output_path.set_extension("dylib");
|
||||
|
||||
("-dylib", output_path)
|
||||
(
|
||||
vec!["-dylib", "-undefined", "dynamic_lookup", "-no_fixup_chains"],
|
||||
output_path,
|
||||
)
|
||||
}
|
||||
LinkType::None => internal_error!("link_macos should not be called with link type of none"),
|
||||
};
|
||||
@ -1272,13 +1275,13 @@ fn link_macos(
|
||||
// The `-l` flags should go after the `.o` arguments
|
||||
// Don't allow LD_ env vars to affect this
|
||||
.env_clear()
|
||||
.args(&link_type_args)
|
||||
.args([
|
||||
// NOTE: we don't do --gc-sections on macOS because the default
|
||||
// macOS linker doesn't support it, but it's a performance
|
||||
// optimization, so if we ever switch to a different linker,
|
||||
// we'd like to re-enable it on macOS!
|
||||
// "--gc-sections",
|
||||
link_type_arg,
|
||||
"-arch",
|
||||
&arch,
|
||||
"-macos_version_min",
|
||||
|
@ -237,15 +237,94 @@ fn gen_from_mono_module_llvm<'a>(
|
||||
|
||||
// annotate the LLVM IR output with debug info
|
||||
// so errors are reported with the line number of the LLVM source
|
||||
let memory_buffer = if emit_debug_info {
|
||||
let memory_buffer = if cfg!(feature = "sanitizers") && std::env::var("ROC_SANITIZERS").is_ok() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let dir = dir.into_path();
|
||||
|
||||
let app_ll_file = dir.join("app.ll");
|
||||
let app_bc_file = dir.join("app.bc");
|
||||
let app_o_file = dir.join("app.o");
|
||||
|
||||
// write the ll code to a file, so we can modify it
|
||||
module.print_to_file(&app_ll_file).unwrap();
|
||||
|
||||
// Apply coverage passes.
|
||||
// Note, this is specifically tailored for `cargo afl` and afl++.
|
||||
// It most likely will not work with other fuzzer setups without modification.
|
||||
let mut passes = vec![];
|
||||
let mut extra_args = vec![];
|
||||
let mut unrecognized = vec![];
|
||||
for sanitizer in std::env::var("ROC_SANITIZERS")
|
||||
.unwrap()
|
||||
.split(',')
|
||||
.map(|x| x.trim())
|
||||
{
|
||||
match sanitizer {
|
||||
"address" => passes.push("asan-module"),
|
||||
"memory" => passes.push("msan-module"),
|
||||
"thread" => passes.push("tsan-module"),
|
||||
"fuzzer" => {
|
||||
passes.push("sancov-module");
|
||||
extra_args.extend_from_slice(&[
|
||||
"-sanitizer-coverage-level=3",
|
||||
"-sanitizer-coverage-prune-blocks=0",
|
||||
"-sanitizer-coverage-trace-pc-guard",
|
||||
// This can be used instead of the line above to enable working with `cargo fuzz` and libFuzzer.
|
||||
// "-sanitizer-coverage-inline-8bit-counters",
|
||||
]);
|
||||
}
|
||||
x => unrecognized.push(x.to_owned()),
|
||||
}
|
||||
}
|
||||
if !unrecognized.is_empty() {
|
||||
let out = unrecognized
|
||||
.iter()
|
||||
.map(|x| format!("{:?}", x))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
eprintln!("Unrecognized sanitizer: {}\nSupported options are \"address\", \"memory\", \"thread\", and \"fuzzer\"", out);
|
||||
}
|
||||
|
||||
use std::process::Command;
|
||||
let mut opt = Command::new("opt");
|
||||
opt.args([
|
||||
app_ll_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
])
|
||||
.args(extra_args);
|
||||
if !passes.is_empty() {
|
||||
opt.arg(format!("-passes={}", passes.join(",")));
|
||||
}
|
||||
let opt = opt.output().unwrap();
|
||||
|
||||
assert!(opt.stderr.is_empty(), "{:#?}", opt);
|
||||
|
||||
// write the .o file. Note that this builds the .o for the local machine,
|
||||
// and ignores the `target_machine` entirely.
|
||||
//
|
||||
// different systems name this executable differently, so we shotgun for
|
||||
// the most common ones and then give up.
|
||||
let bc_to_object = Command::new("llc")
|
||||
.args(&[
|
||||
"-relocation-model=pic",
|
||||
"-filetype=obj",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_o_file.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(bc_to_object.status.success(), "{:#?}", bc_to_object);
|
||||
|
||||
MemoryBuffer::create_from_file(&app_o_file).expect("memory buffer creation works")
|
||||
} else if emit_debug_info {
|
||||
module.strip_debug_info();
|
||||
|
||||
let mut app_ll_dbg_file = PathBuf::from(roc_file_path);
|
||||
app_ll_dbg_file.set_extension("dbg.ll");
|
||||
|
||||
let mut app_bc_file = PathBuf::from(roc_file_path);
|
||||
app_bc_file.set_extension("bc");
|
||||
|
||||
let mut app_o_file = PathBuf::from(roc_file_path);
|
||||
app_o_file.set_extension("o");
|
||||
|
||||
@ -277,33 +356,23 @@ fn gen_from_mono_module_llvm<'a>(
|
||||
| Architecture::X86_32(_)
|
||||
| Architecture::Aarch64(_)
|
||||
| Architecture::Wasm32 => {
|
||||
let ll_to_bc = Command::new("llvm-as")
|
||||
.args([
|
||||
app_ll_dbg_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(ll_to_bc.stderr.is_empty(), "{:#?}", ll_to_bc);
|
||||
|
||||
let llc_args = &[
|
||||
"-relocation-model=pic",
|
||||
"-filetype=obj",
|
||||
app_bc_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_o_file.to_str().unwrap(),
|
||||
];
|
||||
|
||||
// write the .o file. Note that this builds the .o for the local machine,
|
||||
// and ignores the `target_machine` entirely.
|
||||
//
|
||||
// different systems name this executable differently, so we shotgun for
|
||||
// the most common ones and then give up.
|
||||
let bc_to_object = Command::new("llc").args(llc_args).output().unwrap();
|
||||
let ll_to_object = Command::new("llc")
|
||||
.args(&[
|
||||
"-relocation-model=pic",
|
||||
"-filetype=obj",
|
||||
app_ll_dbg_file.to_str().unwrap(),
|
||||
"-o",
|
||||
app_o_file.to_str().unwrap(),
|
||||
])
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
assert!(bc_to_object.stderr.is_empty(), "{:#?}", bc_to_object);
|
||||
assert!(ll_to_object.stderr.is_empty(), "{:#?}", ll_to_object);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -73,7 +73,6 @@ pub enum DecWidth {
|
||||
pub enum FloatWidth {
|
||||
F32,
|
||||
F64,
|
||||
F128,
|
||||
}
|
||||
|
||||
impl FloatWidth {
|
||||
@ -86,7 +85,6 @@ impl FloatWidth {
|
||||
match self {
|
||||
F32 => 4,
|
||||
F64 => 8,
|
||||
F128 => 16,
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +97,7 @@ impl FloatWidth {
|
||||
// the compiler is targeting (e.g. what the Roc code will be compiled to).
|
||||
match self {
|
||||
F32 => 4,
|
||||
F64 | F128 => match target_info.architecture {
|
||||
F64 => match target_info.architecture {
|
||||
X86_64 | Aarch64 | Wasm32 => 8,
|
||||
X86_32 | Aarch32 => 4,
|
||||
},
|
||||
@ -225,7 +223,6 @@ impl Index<FloatWidth> for IntrinsicName {
|
||||
match index {
|
||||
FloatWidth::F32 => self.options[1],
|
||||
FloatWidth::F64 => self.options[2],
|
||||
FloatWidth::F128 => self.options[3],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -256,7 +253,6 @@ macro_rules! float_intrinsic {
|
||||
|
||||
output.options[1] = concat!($name, ".f32");
|
||||
output.options[2] = concat!($name, ".f64");
|
||||
output.options[3] = concat!($name, ".f128");
|
||||
|
||||
output
|
||||
}};
|
||||
|
@ -21,6 +21,8 @@ bumpalo.workspace = true
|
||||
static_assertions.workspace = true
|
||||
bitvec.workspace = true
|
||||
|
||||
ven_pretty = { path = "../../vendor/pretty" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
indoc.workspace = true
|
||||
|
5
crates/compiler/can/src/debug.rs
Normal file
5
crates/compiler/can/src/debug.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod pretty_print;
|
||||
|
||||
pub use pretty_print::pretty_print_declarations;
|
||||
pub use pretty_print::pretty_print_def;
|
||||
pub use pretty_print::Ctx as PPCtx;
|
@ -1,16 +1,50 @@
|
||||
//! Pretty-prints the canonical AST back to check our work - do things look reasonable?
|
||||
|
||||
use roc_can::def::Def;
|
||||
use roc_can::expr::Expr::{self, *};
|
||||
use roc_can::expr::{ClosureData, OpaqueWrapFunctionData, WhenBranch};
|
||||
use roc_can::pattern::{Pattern, RecordDestruct};
|
||||
use crate::def::Def;
|
||||
use crate::expr::Expr::{self, *};
|
||||
use crate::expr::{
|
||||
ClosureData, DeclarationTag, Declarations, FunctionDef, OpaqueWrapFunctionData, WhenBranch,
|
||||
};
|
||||
use crate::pattern::{Pattern, RecordDestruct};
|
||||
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
|
||||
use ven_pretty::{Arena, DocAllocator, DocBuilder};
|
||||
|
||||
pub struct Ctx<'a> {
|
||||
pub home: ModuleId,
|
||||
pub interns: &'a Interns,
|
||||
pub print_lambda_names: bool,
|
||||
}
|
||||
|
||||
pub fn pretty_print_declarations(c: &Ctx, declarations: &Declarations) -> String {
|
||||
let f = Arena::new();
|
||||
let mut defs = Vec::with_capacity(declarations.len());
|
||||
for (index, tag) in declarations.iter_bottom_up() {
|
||||
let symbol = declarations.symbols[index].value;
|
||||
let body = &declarations.expressions[index];
|
||||
|
||||
let def = match tag {
|
||||
DeclarationTag::Value => def_symbol_help(c, &f, symbol, &body.value),
|
||||
DeclarationTag::Function(f_index)
|
||||
| DeclarationTag::Recursive(f_index)
|
||||
| DeclarationTag::TailRecursive(f_index) => {
|
||||
let function_def = &declarations.function_bodies[f_index.index()].value;
|
||||
toplevel_function(c, &f, symbol, function_def, &body.value)
|
||||
}
|
||||
DeclarationTag::Expectation => todo!(),
|
||||
DeclarationTag::ExpectationFx => todo!(),
|
||||
DeclarationTag::Destructure(_) => todo!(),
|
||||
DeclarationTag::MutualRecursion { .. } => todo!(),
|
||||
};
|
||||
|
||||
defs.push(def);
|
||||
}
|
||||
|
||||
f.intersperse(defs, f.hardline().append(f.hardline()))
|
||||
.1
|
||||
.pretty(80)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn pretty_print_def(c: &Ctx, d: &Def) -> String {
|
||||
@ -40,10 +74,58 @@ fn def<'a>(c: &Ctx, f: &'a Arena<'a>, d: &'a Def) -> DocBuilder<'a, Arena<'a>> {
|
||||
annotation: _,
|
||||
} = d;
|
||||
|
||||
pattern(c, PPrec::Free, f, &loc_pattern.value)
|
||||
def_help(c, f, &loc_pattern.value, &loc_expr.value)
|
||||
}
|
||||
|
||||
fn def_symbol_help<'a>(
|
||||
c: &Ctx,
|
||||
f: &'a Arena<'a>,
|
||||
sym: Symbol,
|
||||
body: &'a Expr,
|
||||
) -> DocBuilder<'a, Arena<'a>> {
|
||||
pp_sym(c, f, sym)
|
||||
.append(f.text(" ="))
|
||||
.append(f.line())
|
||||
.append(expr(c, EPrec::Free, f, &loc_expr.value))
|
||||
.append(expr(c, EPrec::Free, f, body))
|
||||
.nest(2)
|
||||
.group()
|
||||
}
|
||||
|
||||
fn def_help<'a>(
|
||||
c: &Ctx,
|
||||
f: &'a Arena<'a>,
|
||||
pat: &'a Pattern,
|
||||
body: &'a Expr,
|
||||
) -> DocBuilder<'a, Arena<'a>> {
|
||||
pattern(c, PPrec::Free, f, pat)
|
||||
.append(f.text(" ="))
|
||||
.append(f.line())
|
||||
.append(expr(c, EPrec::Free, f, body))
|
||||
.nest(2)
|
||||
.group()
|
||||
}
|
||||
|
||||
fn toplevel_function<'a>(
|
||||
c: &Ctx,
|
||||
f: &'a Arena<'a>,
|
||||
sym: Symbol,
|
||||
function_def: &'a FunctionDef,
|
||||
body: &'a Expr,
|
||||
) -> DocBuilder<'a, Arena<'a>> {
|
||||
let FunctionDef { arguments, .. } = function_def;
|
||||
|
||||
let args = arguments
|
||||
.iter()
|
||||
.map(|arg| pattern(c, PPrec::Free, f, &arg.2.value));
|
||||
|
||||
pp_sym(c, f, sym)
|
||||
.append(f.text(" ="))
|
||||
.append(f.line())
|
||||
.append(f.text("\\"))
|
||||
.append(f.intersperse(args, f.text(", ")))
|
||||
.append(f.text("->"))
|
||||
.append(f.line())
|
||||
.append(expr(c, EPrec::Free, f, body))
|
||||
.nest(2)
|
||||
.group()
|
||||
}
|
||||
@ -87,11 +169,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||
.append("]")
|
||||
.group(),
|
||||
),
|
||||
Var(sym, _) | AbilityMember(sym, _, _) => f.text(format!(
|
||||
"{}.{}",
|
||||
sym.module_string(c.interns),
|
||||
sym.as_str(c.interns),
|
||||
)),
|
||||
Var(sym, _) | AbilityMember(sym, _, _) => pp_sym(c, f, *sym),
|
||||
When {
|
||||
loc_cond, branches, ..
|
||||
} => maybe_paren!(
|
||||
@ -184,6 +262,7 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||
Closure(ClosureData {
|
||||
arguments,
|
||||
loc_body,
|
||||
name,
|
||||
..
|
||||
}) => f
|
||||
.text("\\")
|
||||
@ -195,7 +274,13 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||
f.text(", "),
|
||||
),
|
||||
)
|
||||
.append(f.text(" ->"))
|
||||
.append(if c.print_lambda_names {
|
||||
f.text(" -[")
|
||||
.append(pp_sym(c, f, *name))
|
||||
.append(f.text("]->"))
|
||||
} else {
|
||||
f.text(" ->")
|
||||
})
|
||||
.append(f.line())
|
||||
.append(expr(c, Free, f, &loc_body.value))
|
||||
.nest(2)
|
||||
@ -290,6 +375,18 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
||||
}
|
||||
}
|
||||
|
||||
fn pp_sym<'a>(c: &Ctx, f: &'a Arena<'a>, sym: Symbol) -> DocBuilder<'a, Arena<'a>> {
|
||||
if sym.module_id() == c.home {
|
||||
f.text(sym.as_str(c.interns).to_owned())
|
||||
} else {
|
||||
f.text(format!(
|
||||
"{}.{}",
|
||||
sym.module_string(c.interns),
|
||||
sym.as_str(c.interns),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn branch<'a>(c: &Ctx, f: &'a Arena<'a>, b: &'a WhenBranch) -> DocBuilder<'a, Arena<'a>> {
|
||||
let WhenBranch {
|
||||
patterns,
|
||||
@ -333,11 +430,7 @@ fn pattern<'a>(
|
||||
Identifier(sym)
|
||||
| AbilityMemberSpecialization {
|
||||
specializes: sym, ..
|
||||
} => f.text(format!(
|
||||
"{}.{}",
|
||||
sym.module_string(c.interns),
|
||||
sym.as_str(c.interns),
|
||||
)),
|
||||
} => pp_sym(c, f, *sym),
|
||||
AppliedTag {
|
||||
tag_name,
|
||||
arguments,
|
||||
@ -373,12 +466,12 @@ fn pattern<'a>(
|
||||
f.intersperse(
|
||||
destructs.iter().map(|l| &l.value).map(
|
||||
|RecordDestruct { label, typ, .. }| match typ {
|
||||
roc_can::pattern::DestructType::Required => f.text(label.as_str()),
|
||||
roc_can::pattern::DestructType::Optional(_, e) => f
|
||||
crate::pattern::DestructType::Required => f.text(label.as_str()),
|
||||
crate::pattern::DestructType::Optional(_, e) => f
|
||||
.text(label.as_str())
|
||||
.append(f.text(" ? "))
|
||||
.append(expr(c, EPrec::Free, f, &e.value)),
|
||||
roc_can::pattern::DestructType::Guard(_, p) => f
|
||||
crate::pattern::DestructType::Guard(_, p) => f
|
||||
.text(label.as_str())
|
||||
.append(f.text(": "))
|
||||
.append(pattern(c, Free, f, &p.value)),
|
@ -28,3 +28,5 @@ pub mod string;
|
||||
pub mod traverse;
|
||||
|
||||
pub use derive::DERIVED_REGION;
|
||||
|
||||
pub mod debug;
|
||||
|
@ -705,7 +705,7 @@ pub fn constrain_expr(
|
||||
expected,
|
||||
);
|
||||
|
||||
constraints.exists_many([], [cond_con, continuation_con])
|
||||
constraints.exists_many([*variable], [cond_con, continuation_con])
|
||||
}
|
||||
|
||||
If {
|
||||
|
@ -709,7 +709,6 @@ fn float_with_precision<'a, 'ctx, 'env>(
|
||||
match float_width {
|
||||
FloatWidth::F64 => env.context.f64_type().const_float(value).into(),
|
||||
FloatWidth::F32 => env.context.f32_type().const_float(value).into(),
|
||||
FloatWidth::F128 => todo!("F128 is not implemented"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -2586,7 +2585,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
condition: cond_symbol,
|
||||
region,
|
||||
lookups,
|
||||
layouts: _,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let bd = env.builder;
|
||||
@ -2621,6 +2620,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
*cond_symbol,
|
||||
*region,
|
||||
lookups,
|
||||
variables,
|
||||
);
|
||||
|
||||
if let LlvmBackendMode::BinaryDev = env.mode {
|
||||
@ -2655,7 +2655,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
condition: cond_symbol,
|
||||
region,
|
||||
lookups,
|
||||
layouts: _,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let bd = env.builder;
|
||||
@ -2690,6 +2690,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
*cond_symbol,
|
||||
*region,
|
||||
lookups,
|
||||
variables,
|
||||
);
|
||||
|
||||
bd.build_unconditional_branch(then_block);
|
||||
@ -3036,7 +3037,6 @@ fn build_switch_ir<'a, 'ctx, 'env>(
|
||||
let int_type = match float_width {
|
||||
FloatWidth::F32 => env.context.i32_type(),
|
||||
FloatWidth::F64 => env.context.i64_type(),
|
||||
FloatWidth::F128 => env.context.i128_type(),
|
||||
};
|
||||
|
||||
builder
|
||||
|
@ -111,7 +111,6 @@ fn build_eq_builtin<'a, 'ctx, 'env>(
|
||||
use FloatWidth::*;
|
||||
|
||||
let name = match float_width {
|
||||
F128 => "eq_f128",
|
||||
F64 => "eq_f64",
|
||||
F32 => "eq_f32",
|
||||
};
|
||||
@ -276,7 +275,6 @@ fn build_neq_builtin<'a, 'ctx, 'env>(
|
||||
use FloatWidth::*;
|
||||
|
||||
let name = match float_width {
|
||||
F128 => "neq_f128",
|
||||
F64 => "neq_f64",
|
||||
F32 => "neq_f32",
|
||||
};
|
||||
|
@ -202,7 +202,6 @@ pub fn float_type_from_float_width<'a, 'ctx, 'env>(
|
||||
use FloatWidth::*;
|
||||
|
||||
match float_width {
|
||||
F128 => todo!("F128 is not implemented"),
|
||||
F64 => env.context.f64_type(),
|
||||
F32 => env.context.f32_type(),
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
|
||||
use inkwell::AddressSpace;
|
||||
use roc_builtins::bitcode;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::ir::LookupType;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, UnionLayout};
|
||||
use roc_region::all::Region;
|
||||
|
||||
@ -143,6 +144,21 @@ pub(crate) fn notify_parent_dbg(env: &Env, shared_memory: &SharedMemoryPointer)
|
||||
);
|
||||
}
|
||||
|
||||
// Shape of expect frame:
|
||||
//
|
||||
// ===
|
||||
// Fixed-size header
|
||||
// ===
|
||||
// /-- ptr_lookup_1 (ptr_size)
|
||||
// | var_lookup_1 (u32)
|
||||
// | ..
|
||||
// | ptr_lookup_n (ptr_size)
|
||||
// | var_lookup_n (u32)
|
||||
// \-> lookup_val_1 (varsize)
|
||||
// ..
|
||||
// lookup_val_n (varsize)
|
||||
//
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
scope: &Scope<'a, 'ctx>,
|
||||
@ -151,6 +167,7 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||
condition: Symbol,
|
||||
region: Region,
|
||||
lookups: &[Symbol],
|
||||
lookup_variables: &[LookupType],
|
||||
) {
|
||||
let original_ptr = shared_memory.0;
|
||||
|
||||
@ -160,9 +177,11 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||
|
||||
let after_header = offset;
|
||||
|
||||
let space_for_offsets = env
|
||||
.ptr_int()
|
||||
.const_int((lookups.len() * env.target_info.ptr_size()) as _, false);
|
||||
let space_for_offsets = env.ptr_int().const_int(
|
||||
(lookups.len() * env.target_info.ptr_size() + lookups.len() * std::mem::size_of::<u32>())
|
||||
as _,
|
||||
false,
|
||||
);
|
||||
|
||||
let mut lookup_starts = bumpalo::collections::Vec::with_capacity_in(lookups.len(), env.arena);
|
||||
|
||||
@ -203,14 +222,43 @@ pub(crate) fn clone_to_shared_memory<'a, 'ctx, 'env>(
|
||||
{
|
||||
let mut offset = after_header;
|
||||
|
||||
for lookup_start in lookup_starts {
|
||||
build_copy(env, original_ptr, offset, lookup_start.into());
|
||||
for (lookup_start, lookup_var) in lookup_starts.into_iter().zip(lookup_variables) {
|
||||
// Store the pointer to the value
|
||||
{
|
||||
build_copy(env, original_ptr, offset, lookup_start.into());
|
||||
|
||||
let ptr_width = env
|
||||
.ptr_int()
|
||||
.const_int(env.target_info.ptr_size() as _, false);
|
||||
let ptr_width = env
|
||||
.ptr_int()
|
||||
.const_int(env.target_info.ptr_size() as _, false);
|
||||
|
||||
offset = env.builder.build_int_add(offset, ptr_width, "offset")
|
||||
offset = env.builder.build_int_add(offset, ptr_width, "offset");
|
||||
}
|
||||
|
||||
// Store the specialized variable of the value
|
||||
{
|
||||
let ptr = unsafe {
|
||||
env.builder
|
||||
.build_in_bounds_gep(original_ptr, &[offset], "at_current_offset")
|
||||
};
|
||||
|
||||
let u32_ptr = env.context.i32_type().ptr_type(AddressSpace::Generic);
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_pointer_cast(ptr, u32_ptr, "cast_ptr_type");
|
||||
|
||||
let var_value = env
|
||||
.context
|
||||
.i32_type()
|
||||
.const_int(lookup_var.index() as _, false);
|
||||
|
||||
env.builder.build_store(ptr, var_value);
|
||||
|
||||
let var_size = env
|
||||
.ptr_int()
|
||||
.const_int(std::mem::size_of::<u32>() as _, false);
|
||||
|
||||
offset = env.builder.build_int_add(offset, var_size, "offset");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ fn add_float_intrinsic<'ctx, F>(
|
||||
|
||||
check!(FloatWidth::F32, ctx.f32_type());
|
||||
check!(FloatWidth::F64, ctx.f64_type());
|
||||
// check!(IntWidth::F128, ctx.i128_type());
|
||||
}
|
||||
|
||||
fn add_int_intrinsic<'ctx, F>(
|
||||
|
@ -11,7 +11,7 @@ use roc_builtins::bitcode::{self, FloatWidth, IntWidth};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_module::{low_level::LowLevel, symbol::Symbol};
|
||||
use roc_mono::{
|
||||
ir::HigherOrderLowLevel,
|
||||
ir::{HigherOrderLowLevel, LookupType},
|
||||
layout::{Builtin, LambdaSet, Layout, LayoutIds},
|
||||
};
|
||||
use roc_target::PtrWidth;
|
||||
@ -1120,14 +1120,19 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
||||
}
|
||||
},
|
||||
Dbg => {
|
||||
// now what
|
||||
arguments!(condition);
|
||||
assert_eq!(args.len(), 2);
|
||||
let condition = load_symbol(scope, &args[0]);
|
||||
let dbg_spec_var_symbol = args[1];
|
||||
|
||||
if env.mode.runs_expects() {
|
||||
let region = unsafe { std::mem::transmute::<_, roc_region::all::Region>(args[0]) };
|
||||
|
||||
let shared_memory = crate::llvm::expect::SharedMemoryPointer::get(env);
|
||||
|
||||
// HACK(dbg-spec-var): the specialized type variable is passed along as a fake symbol
|
||||
let specialized_var =
|
||||
unsafe { LookupType::from_index(dbg_spec_var_symbol.ident_id().index() as _) };
|
||||
|
||||
crate::llvm::expect::clone_to_shared_memory(
|
||||
env,
|
||||
scope,
|
||||
@ -1136,6 +1141,7 @@ pub(crate) fn run_low_level<'a, 'ctx, 'env>(
|
||||
args[0],
|
||||
region,
|
||||
&[args[0]],
|
||||
&[specialized_var],
|
||||
);
|
||||
|
||||
crate::llvm::expect::notify_parent_dbg(env, &shared_memory);
|
||||
@ -2124,13 +2130,6 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||
"f64_to_f32",
|
||||
),
|
||||
(FloatWidth::F64, FloatWidth::F64) => arg.into(),
|
||||
(FloatWidth::F128, FloatWidth::F128) => arg.into(),
|
||||
(FloatWidth::F128, _) => {
|
||||
unimplemented!("I cannot handle F128 with Num.toFrac yet")
|
||||
}
|
||||
(_, FloatWidth::F128) => {
|
||||
unimplemented!("I cannot handle F128 with Num.toFrac yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
NumCeiling => {
|
||||
|
@ -21,7 +21,6 @@ pub enum StackMemoryFormat {
|
||||
/// Record, Str, List, etc.
|
||||
DataStructure,
|
||||
Int128,
|
||||
Float128,
|
||||
Decimal,
|
||||
}
|
||||
|
||||
@ -71,11 +70,6 @@ impl WasmLayout {
|
||||
match float_width {
|
||||
F32 => Self::Primitive(ValueType::F32, size),
|
||||
F64 => Self::Primitive(ValueType::F64, size),
|
||||
F128 => Self::StackMemory {
|
||||
size,
|
||||
alignment_bytes,
|
||||
format: StackMemoryFormat::Float128,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +150,7 @@ impl CallConv {
|
||||
use ValueType::*;
|
||||
|
||||
match format {
|
||||
Int128 | Float128 | Decimal => &[I64, I64],
|
||||
Int128 | Decimal => &[I64, I64],
|
||||
|
||||
DataStructure => {
|
||||
if size == 0 {
|
||||
@ -191,7 +185,7 @@ impl CallConv {
|
||||
use StackMemoryFormat::*;
|
||||
|
||||
match format {
|
||||
Int128 | Float128 | Decimal => WriteToPointerArg,
|
||||
Int128 | Decimal => WriteToPointerArg,
|
||||
|
||||
DataStructure => {
|
||||
if size == 0 {
|
||||
|
@ -30,7 +30,6 @@ enum CodeGenNumType {
|
||||
F32, // Supported in Wasm instruction set
|
||||
F64, // Supported in Wasm instruction set
|
||||
I128, // Bytes in memory, needs Zig builtins
|
||||
F128, // Bytes in memory, needs Zig builtins
|
||||
Decimal, // Bytes in memory, needs Zig builtins
|
||||
}
|
||||
|
||||
@ -66,7 +65,6 @@ impl From<Layout<'_>> for CodeGenNumType {
|
||||
Builtin::Float(float_width) => match float_width {
|
||||
FloatWidth::F32 => F32,
|
||||
FloatWidth::F64 => F64,
|
||||
FloatWidth::F128 => F128,
|
||||
},
|
||||
Builtin::Decimal => Decimal,
|
||||
_ => not_num_error(),
|
||||
@ -91,7 +89,6 @@ impl From<StackMemoryFormat> for CodeGenNumType {
|
||||
fn from(format: StackMemoryFormat) -> CodeGenNumType {
|
||||
match format {
|
||||
StackMemoryFormat::Int128 => CodeGenNumType::I128,
|
||||
StackMemoryFormat::Float128 => CodeGenNumType::F128,
|
||||
StackMemoryFormat::Decimal => CodeGenNumType::Decimal,
|
||||
StackMemoryFormat::DataStructure => {
|
||||
internal_error!("Tried to perform a Num low-level operation on a data structure")
|
||||
@ -804,7 +801,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_add()
|
||||
}
|
||||
FloatWidth::F128 => todo!("Num.add for f128"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
self.load_args_and_call_zig(backend, bitcode::DEC_ADD_OR_PANIC)
|
||||
@ -841,7 +837,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_add()
|
||||
}
|
||||
FloatWidth::F128 => todo!("Num.add for f128"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
// TODO: don't panic
|
||||
@ -897,7 +892,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_sub()
|
||||
}
|
||||
FloatWidth::F128 => todo!("Num.sub for f128"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
self.load_args_and_call_zig(backend, bitcode::DEC_SUB_OR_PANIC)
|
||||
@ -934,7 +928,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_sub()
|
||||
}
|
||||
FloatWidth::F128 => todo!("Num.sub for f128"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
// TODO: don't panic
|
||||
@ -988,7 +981,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_mul()
|
||||
}
|
||||
FloatWidth::F128 => todo!("Num.mul for f128"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
self.load_args_and_call_zig(backend, bitcode::DEC_MUL_OR_PANIC)
|
||||
@ -1024,7 +1016,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
self.load_args(backend);
|
||||
backend.code_builder.f64_mul()
|
||||
}
|
||||
FloatWidth::F128 => todo!("Num.mul for f128"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
// TODO: don't panic
|
||||
@ -1466,9 +1457,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F64)) => {
|
||||
backend.code_builder.f64_sqrt()
|
||||
}
|
||||
Layout::Builtin(Builtin::Float(FloatWidth::F128)) => {
|
||||
todo!("sqrt for f128")
|
||||
}
|
||||
_ => panic_ret_type(),
|
||||
}
|
||||
}
|
||||
@ -2009,8 +1997,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
|
||||
StackMemoryFormat::Int128 => Self::eq_num128_bytes(backend, locations),
|
||||
|
||||
StackMemoryFormat::Float128 => todo!("equality for f128"),
|
||||
|
||||
StackMemoryFormat::DataStructure => {
|
||||
internal_error!("Data structure equality is handled elsewhere")
|
||||
}
|
||||
@ -2057,7 +2043,6 @@ impl<'a> LowLevelCall<'a> {
|
||||
FloatWidth::F64 => {
|
||||
self.load_args_and_call_zig(backend, &bitcode::STR_FROM_FLOAT[width]);
|
||||
}
|
||||
FloatWidth::F128 => todo!("F128 to Str"),
|
||||
},
|
||||
Layout::Builtin(Builtin::Decimal) => {
|
||||
self.load_args_and_call_zig(backend, bitcode::DEC_TO_STR)
|
||||
@ -2095,27 +2080,13 @@ fn num_is_finite(backend: &mut WasmBackend<'_>, argument: Symbol) {
|
||||
}
|
||||
}
|
||||
}
|
||||
StackMemory {
|
||||
format, location, ..
|
||||
} => {
|
||||
let (local_id, offset) = location.local_and_offset(backend.storage.stack_frame_pointer);
|
||||
|
||||
StackMemory { format, .. } => {
|
||||
match format {
|
||||
// Integers and fixed-point numbers are always finite. Just return True.
|
||||
StackMemoryFormat::Int128 | StackMemoryFormat::Decimal => {
|
||||
backend.code_builder.i32_const(1)
|
||||
}
|
||||
|
||||
// f128 is not supported anywhere else but it's easy to support it here, so why not...
|
||||
StackMemoryFormat::Float128 => {
|
||||
backend.code_builder.get_local(local_id);
|
||||
backend.code_builder.i64_load(Align::Bytes4, offset + 8);
|
||||
backend.code_builder.i64_const(0x7fff_0000_0000_0000);
|
||||
backend.code_builder.i64_and();
|
||||
backend.code_builder.i64_const(0x7fff_0000_0000_0000);
|
||||
backend.code_builder.i64_ne();
|
||||
}
|
||||
|
||||
StackMemoryFormat::DataStructure => {
|
||||
internal_error!("Tried to perform NumIsFinite on a data structure")
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ impl<'a> Storage<'a> {
|
||||
.extend_from_slice(CallConv::C.stack_memory_arg_types(size, format));
|
||||
|
||||
let location = match format {
|
||||
Int128 | Float128 | Decimal => {
|
||||
Int128 | Decimal => {
|
||||
// passed as two i64's but stored in the stack frame
|
||||
wide_number_args.push(local_index);
|
||||
let loc =
|
||||
|
@ -138,7 +138,6 @@ struct ModuleCache<'a> {
|
||||
found_specializations: MutMap<ModuleId, FoundSpecializationsModule<'a>>,
|
||||
late_specializations: MutMap<ModuleId, LateSpecializationsModule<'a>>,
|
||||
external_specializations_requested: MutMap<ModuleId, Vec<ExternalSpecializations<'a>>>,
|
||||
expectations: VecMap<ModuleId, Expectations>,
|
||||
|
||||
/// Various information
|
||||
imports: MutMap<ModuleId, MutSet<ModuleId>>,
|
||||
@ -215,7 +214,6 @@ impl Default for ModuleCache<'_> {
|
||||
can_problems: Default::default(),
|
||||
type_problems: Default::default(),
|
||||
sources: Default::default(),
|
||||
expectations: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -435,6 +433,7 @@ fn start_phase<'a>(
|
||||
decls,
|
||||
ident_ids,
|
||||
abilities_store,
|
||||
expectations,
|
||||
} = typechecked;
|
||||
|
||||
let mut imported_module_thunks = bumpalo::collections::Vec::new_in(arena);
|
||||
@ -451,8 +450,8 @@ fn start_phase<'a>(
|
||||
|
||||
let derived_module = SharedDerivedModule::clone(&state.derived_module);
|
||||
|
||||
let build_expects = matches!(state.exec_mode, ExecutionMode::Test)
|
||||
&& state.module_cache.expectations.contains_key(&module_id);
|
||||
let build_expects =
|
||||
matches!(state.exec_mode, ExecutionMode::Test) && expectations.is_some();
|
||||
|
||||
BuildTask::BuildPendingSpecializations {
|
||||
layout_cache,
|
||||
@ -467,6 +466,7 @@ fn start_phase<'a>(
|
||||
// TODO: awful, how can we get rid of the clone?
|
||||
exposed_by_module: state.exposed_types.clone(),
|
||||
derived_module,
|
||||
expectations,
|
||||
build_expects,
|
||||
}
|
||||
}
|
||||
@ -488,67 +488,90 @@ fn start_phase<'a>(
|
||||
specializations_we_must_make.extend(derived_synth_specializations)
|
||||
}
|
||||
|
||||
let (mut ident_ids, mut subs, mut procs_base, layout_cache, mut module_timing) =
|
||||
if state.make_specializations_pass.current_pass() == 1
|
||||
&& module_id == ModuleId::DERIVED_GEN
|
||||
{
|
||||
// This is the first time the derived module is introduced into the load
|
||||
// graph. It has no abilities of its own or anything, just generate fresh
|
||||
// information for it.
|
||||
(
|
||||
IdentIds::default(),
|
||||
Subs::default(),
|
||||
ProcsBase::default(),
|
||||
LayoutCache::new(state.layout_interner.fork(), state.target_info),
|
||||
ModuleTiming::new(Instant::now()),
|
||||
)
|
||||
} else if state.make_specializations_pass.current_pass() == 1 {
|
||||
let found_specializations = state
|
||||
.module_cache
|
||||
.found_specializations
|
||||
.remove(&module_id)
|
||||
.unwrap();
|
||||
let (
|
||||
mut ident_ids,
|
||||
mut subs,
|
||||
expectations,
|
||||
mut procs_base,
|
||||
layout_cache,
|
||||
mut module_timing,
|
||||
) = if state.make_specializations_pass.current_pass() == 1
|
||||
&& module_id == ModuleId::DERIVED_GEN
|
||||
{
|
||||
// This is the first time the derived module is introduced into the load
|
||||
// graph. It has no abilities of its own or anything, just generate fresh
|
||||
// information for it.
|
||||
(
|
||||
IdentIds::default(),
|
||||
Subs::default(),
|
||||
None, // no expectations for derived module
|
||||
ProcsBase::default(),
|
||||
LayoutCache::new(state.layout_interner.fork(), state.target_info),
|
||||
ModuleTiming::new(Instant::now()),
|
||||
)
|
||||
} else if state.make_specializations_pass.current_pass() == 1 {
|
||||
let found_specializations = state
|
||||
.module_cache
|
||||
.found_specializations
|
||||
.remove(&module_id)
|
||||
.unwrap();
|
||||
|
||||
let FoundSpecializationsModule {
|
||||
ident_ids,
|
||||
subs,
|
||||
procs_base,
|
||||
layout_cache,
|
||||
module_timing,
|
||||
abilities_store,
|
||||
} = found_specializations;
|
||||
let FoundSpecializationsModule {
|
||||
ident_ids,
|
||||
subs,
|
||||
procs_base,
|
||||
layout_cache,
|
||||
module_timing,
|
||||
abilities_store,
|
||||
expectations,
|
||||
} = found_specializations;
|
||||
|
||||
let our_exposed_types = state
|
||||
.exposed_types
|
||||
.get(&module_id)
|
||||
.unwrap_or_else(|| {
|
||||
internal_error!("Exposed types for {:?} missing", module_id)
|
||||
})
|
||||
.clone();
|
||||
let our_exposed_types = state
|
||||
.exposed_types
|
||||
.get(&module_id)
|
||||
.unwrap_or_else(|| {
|
||||
internal_error!("Exposed types for {:?} missing", module_id)
|
||||
})
|
||||
.clone();
|
||||
|
||||
// Add our abilities to the world.
|
||||
state.world_abilities.insert(
|
||||
module_id,
|
||||
abilities_store,
|
||||
our_exposed_types.exposed_types_storage_subs,
|
||||
);
|
||||
// Add our abilities to the world.
|
||||
state.world_abilities.insert(
|
||||
module_id,
|
||||
abilities_store,
|
||||
our_exposed_types.exposed_types_storage_subs,
|
||||
);
|
||||
|
||||
(ident_ids, subs, procs_base, layout_cache, module_timing)
|
||||
} else {
|
||||
let LateSpecializationsModule {
|
||||
ident_ids,
|
||||
subs,
|
||||
module_timing,
|
||||
layout_cache,
|
||||
procs_base,
|
||||
} = state
|
||||
.module_cache
|
||||
.late_specializations
|
||||
.remove(&module_id)
|
||||
.unwrap();
|
||||
(
|
||||
ident_ids,
|
||||
subs,
|
||||
expectations,
|
||||
procs_base,
|
||||
layout_cache,
|
||||
module_timing,
|
||||
)
|
||||
} else {
|
||||
let LateSpecializationsModule {
|
||||
ident_ids,
|
||||
subs,
|
||||
expectations,
|
||||
module_timing,
|
||||
layout_cache,
|
||||
procs_base,
|
||||
} = state
|
||||
.module_cache
|
||||
.late_specializations
|
||||
.remove(&module_id)
|
||||
.unwrap();
|
||||
|
||||
(ident_ids, subs, procs_base, layout_cache, module_timing)
|
||||
};
|
||||
(
|
||||
ident_ids,
|
||||
subs,
|
||||
expectations,
|
||||
procs_base,
|
||||
layout_cache,
|
||||
module_timing,
|
||||
)
|
||||
};
|
||||
|
||||
if module_id == ModuleId::DERIVED_GEN {
|
||||
load_derived_partial_procs(
|
||||
@ -579,6 +602,7 @@ fn start_phase<'a>(
|
||||
// TODO: awful, how can we get rid of the clone?
|
||||
exposed_by_module: state.exposed_types.clone(),
|
||||
derived_module,
|
||||
expectations,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -679,6 +703,7 @@ pub struct TypeCheckedModule<'a> {
|
||||
pub decls: Declarations,
|
||||
pub ident_ids: IdentIds,
|
||||
pub abilities_store: AbilitiesStore,
|
||||
pub expectations: Option<Expectations>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -689,6 +714,7 @@ struct FoundSpecializationsModule<'a> {
|
||||
subs: Subs,
|
||||
module_timing: ModuleTiming,
|
||||
abilities_store: AbilitiesStore,
|
||||
expectations: Option<Expectations>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -698,6 +724,7 @@ struct LateSpecializationsModule<'a> {
|
||||
module_timing: ModuleTiming,
|
||||
layout_cache: LayoutCache<'a>,
|
||||
procs_base: ProcsBase<'a>,
|
||||
expectations: Option<Expectations>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -831,6 +858,7 @@ enum Msg<'a> {
|
||||
module_timing: ModuleTiming,
|
||||
abilities_store: AbilitiesStore,
|
||||
toplevel_expects: ToplevelExpects,
|
||||
expectations: Option<Expectations>,
|
||||
},
|
||||
MadeSpecializations {
|
||||
module_id: ModuleId,
|
||||
@ -842,6 +870,7 @@ enum Msg<'a> {
|
||||
update_mode_ids: UpdateModeIds,
|
||||
module_timing: ModuleTiming,
|
||||
subs: Subs,
|
||||
expectations: Option<Expectations>,
|
||||
},
|
||||
|
||||
/// The task is to only typecheck AND monomorphize modules
|
||||
@ -852,6 +881,7 @@ enum Msg<'a> {
|
||||
/// DO NOT use the one on state; that is left in an empty state after specialization is complete!
|
||||
layout_interner: STLayoutInterner<'a>,
|
||||
exposed_to_host: ExposedToHost,
|
||||
module_expectations: VecMap<ModuleId, Expectations>,
|
||||
},
|
||||
|
||||
FailedToParse(FileError<'a, SyntaxError<'a>>),
|
||||
@ -1147,6 +1177,7 @@ enum BuildTask<'a> {
|
||||
exposed_by_module: ExposedByModule,
|
||||
abilities_store: AbilitiesStore,
|
||||
derived_module: SharedDerivedModule,
|
||||
expectations: Option<Expectations>,
|
||||
build_expects: bool,
|
||||
},
|
||||
MakeSpecializations {
|
||||
@ -1160,6 +1191,7 @@ enum BuildTask<'a> {
|
||||
exposed_by_module: ExposedByModule,
|
||||
world_abilities: WorldAbilities,
|
||||
derived_module: SharedDerivedModule,
|
||||
expectations: Option<Expectations>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -1717,6 +1749,7 @@ fn state_thread_step<'a>(
|
||||
subs,
|
||||
layout_interner,
|
||||
exposed_to_host,
|
||||
module_expectations,
|
||||
} => {
|
||||
// We're done! There should be no more messages pending.
|
||||
debug_assert!(msg_rx.is_empty());
|
||||
@ -1727,6 +1760,7 @@ fn state_thread_step<'a>(
|
||||
subs,
|
||||
layout_interner,
|
||||
exposed_to_host,
|
||||
module_expectations,
|
||||
)?;
|
||||
|
||||
Ok(ControlFlow::Break(LoadResult::Monomorphized(monomorphized)))
|
||||
@ -2608,22 +2642,19 @@ fn update<'a>(
|
||||
.expect("root or this module is not yet known - that's a bug!")
|
||||
};
|
||||
|
||||
if should_include_expects {
|
||||
let opt_expectations = if should_include_expects {
|
||||
let (path, _) = state.module_cache.sources.get(&module_id).unwrap();
|
||||
|
||||
let expectations = Expectations {
|
||||
Some(Expectations {
|
||||
expectations: loc_expects,
|
||||
dbgs: loc_dbgs,
|
||||
subs: solved_subs.clone().into_inner(),
|
||||
path: path.to_owned(),
|
||||
ident_ids: ident_ids.clone(),
|
||||
};
|
||||
|
||||
state
|
||||
.module_cache
|
||||
.expectations
|
||||
.insert(module_id, expectations);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let work = state.dependencies.notify(module_id, Phase::SolveTypes);
|
||||
|
||||
@ -2736,6 +2767,7 @@ fn update<'a>(
|
||||
decls,
|
||||
ident_ids,
|
||||
abilities_store,
|
||||
expectations: opt_expectations,
|
||||
};
|
||||
|
||||
state
|
||||
@ -2775,6 +2807,7 @@ fn update<'a>(
|
||||
module_timing,
|
||||
abilities_store,
|
||||
toplevel_expects,
|
||||
expectations,
|
||||
} => {
|
||||
log!("found specializations for {:?}", module_id);
|
||||
|
||||
@ -2797,6 +2830,7 @@ fn update<'a>(
|
||||
subs,
|
||||
module_timing,
|
||||
abilities_store,
|
||||
expectations,
|
||||
};
|
||||
|
||||
state
|
||||
@ -2822,6 +2856,7 @@ fn update<'a>(
|
||||
external_specializations_requested,
|
||||
module_timing,
|
||||
layout_cache,
|
||||
expectations,
|
||||
..
|
||||
} => {
|
||||
debug_assert!(
|
||||
@ -2843,6 +2878,7 @@ fn update<'a>(
|
||||
subs,
|
||||
layout_cache,
|
||||
procs_base,
|
||||
expectations,
|
||||
},
|
||||
);
|
||||
|
||||
@ -2900,6 +2936,9 @@ fn update<'a>(
|
||||
);
|
||||
}
|
||||
|
||||
let mut module_expectations =
|
||||
VecMap::with_capacity(state.module_cache.module_names.len());
|
||||
|
||||
// Flush late-specialization module information to the top-level of the state
|
||||
// where it will be visible to others, since we don't need late specialization
|
||||
// anymore.
|
||||
@ -2911,6 +2950,7 @@ fn update<'a>(
|
||||
module_timing,
|
||||
layout_cache: _layout_cache,
|
||||
procs_base: _,
|
||||
expectations,
|
||||
},
|
||||
) in state.module_cache.late_specializations.drain()
|
||||
{
|
||||
@ -2919,6 +2959,9 @@ fn update<'a>(
|
||||
state.root_subs = Some(subs);
|
||||
}
|
||||
state.timings.insert(module_id, module_timing);
|
||||
if let Some(expectations) = expectations {
|
||||
module_expectations.insert(module_id, expectations);
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
@ -2981,6 +3024,7 @@ fn update<'a>(
|
||||
subs,
|
||||
layout_interner,
|
||||
exposed_to_host: state.exposed_to_host.clone(),
|
||||
module_expectations,
|
||||
})
|
||||
.map_err(|_| LoadingProblem::MsgChannelDied)?;
|
||||
|
||||
@ -3114,6 +3158,7 @@ fn finish_specialization<'a>(
|
||||
subs: Subs,
|
||||
layout_interner: STLayoutInterner<'a>,
|
||||
exposed_to_host: ExposedToHost,
|
||||
module_expectations: VecMap<ModuleId, Expectations>,
|
||||
) -> Result<MonomorphizedModule<'a>, LoadingProblem<'a>> {
|
||||
if false {
|
||||
println!(
|
||||
@ -3154,7 +3199,6 @@ fn finish_specialization<'a>(
|
||||
} = state;
|
||||
|
||||
let ModuleCache {
|
||||
expectations,
|
||||
type_problems,
|
||||
can_problems,
|
||||
sources,
|
||||
@ -3245,7 +3289,7 @@ fn finish_specialization<'a>(
|
||||
can_problems,
|
||||
type_problems,
|
||||
output_path,
|
||||
expectations,
|
||||
expectations: module_expectations,
|
||||
exposed_to_host,
|
||||
module_id: state.root_id,
|
||||
subs,
|
||||
@ -5230,6 +5274,7 @@ fn make_specializations<'a>(
|
||||
world_abilities: WorldAbilities,
|
||||
exposed_by_module: &ExposedByModule,
|
||||
derived_module: SharedDerivedModule,
|
||||
mut expectations: Option<Expectations>,
|
||||
) -> Msg<'a> {
|
||||
let make_specializations_start = Instant::now();
|
||||
let mut update_mode_ids = UpdateModeIds::new();
|
||||
@ -5237,6 +5282,7 @@ fn make_specializations<'a>(
|
||||
let mut mono_env = roc_mono::ir::Env {
|
||||
arena,
|
||||
subs: &mut subs,
|
||||
expectation_subs: expectations.as_mut().map(|e| &mut e.subs),
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
target_info,
|
||||
@ -5288,6 +5334,7 @@ fn make_specializations<'a>(
|
||||
procedures,
|
||||
update_mode_ids,
|
||||
subs,
|
||||
expectations,
|
||||
external_specializations_requested,
|
||||
module_timing,
|
||||
}
|
||||
@ -5307,6 +5354,7 @@ fn build_pending_specializations<'a>(
|
||||
exposed_by_module: &ExposedByModule,
|
||||
abilities_store: AbilitiesStore,
|
||||
derived_module: SharedDerivedModule,
|
||||
mut expectations: Option<Expectations>,
|
||||
build_expects: bool,
|
||||
) -> Msg<'a> {
|
||||
let find_specializations_start = Instant::now();
|
||||
@ -5327,6 +5375,7 @@ fn build_pending_specializations<'a>(
|
||||
let mut mono_env = roc_mono::ir::Env {
|
||||
arena,
|
||||
subs: &mut subs,
|
||||
expectation_subs: expectations.as_mut().map(|e| &mut e.subs),
|
||||
home,
|
||||
ident_ids: &mut ident_ids,
|
||||
target_info,
|
||||
@ -5716,6 +5765,7 @@ fn build_pending_specializations<'a>(
|
||||
module_timing,
|
||||
abilities_store,
|
||||
toplevel_expects,
|
||||
expectations,
|
||||
}
|
||||
}
|
||||
|
||||
@ -5757,6 +5807,8 @@ fn load_derived_partial_procs<'a>(
|
||||
let mut mono_env = roc_mono::ir::Env {
|
||||
arena,
|
||||
subs,
|
||||
// There are no derived expectations.
|
||||
expectation_subs: None,
|
||||
home,
|
||||
ident_ids,
|
||||
target_info,
|
||||
@ -5916,6 +5968,7 @@ fn run_task<'a>(
|
||||
abilities_store,
|
||||
exposed_by_module,
|
||||
derived_module,
|
||||
expectations,
|
||||
build_expects,
|
||||
} => Ok(build_pending_specializations(
|
||||
arena,
|
||||
@ -5931,6 +5984,7 @@ fn run_task<'a>(
|
||||
&exposed_by_module,
|
||||
abilities_store,
|
||||
derived_module,
|
||||
expectations,
|
||||
build_expects,
|
||||
)),
|
||||
MakeSpecializations {
|
||||
@ -5944,6 +5998,7 @@ fn run_task<'a>(
|
||||
world_abilities,
|
||||
exposed_by_module,
|
||||
derived_module,
|
||||
expectations,
|
||||
} => Ok(make_specializations(
|
||||
arena,
|
||||
module_id,
|
||||
@ -5957,6 +6012,7 @@ fn run_task<'a>(
|
||||
world_abilities,
|
||||
&exposed_by_module,
|
||||
derived_module,
|
||||
expectations,
|
||||
)),
|
||||
}?;
|
||||
|
||||
|
@ -594,6 +594,13 @@ impl IdentId {
|
||||
pub const fn index(self) -> usize {
|
||||
self.0 as usize
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The index is not guaranteed to know to exist.
|
||||
pub unsafe fn from_index(index: u32) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores a mapping between Ident and IdentId.
|
||||
|
@ -162,9 +162,15 @@ impl<'a> DeclarationToIndex<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let similar = self
|
||||
.elements
|
||||
.iter()
|
||||
.filter_map(|((s, lay), _)| if *s == needle_symbol { Some(lay) } else { None })
|
||||
.collect::<std::vec::Vec<_>>();
|
||||
unreachable!(
|
||||
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex",
|
||||
needle_symbol, needle_layout
|
||||
"symbol/layout {:?} {:#?} combo must be in DeclarationToIndex\nHowever {} similar layouts were found:\n{:#?}",
|
||||
needle_symbol, needle_layout, similar.len(), similar
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -942,7 +948,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||
|
||||
ListIsUnique => arena.alloc_slice_copy(&[borrowed]),
|
||||
|
||||
Dbg => arena.alloc_slice_copy(&[borrowed]),
|
||||
Dbg => arena.alloc_slice_copy(&[borrowed, /* dbg-spec-var */ irrelevant]),
|
||||
|
||||
BoxExpr | UnboxExpr => {
|
||||
unreachable!("These lowlevel operations are turned into mono Expr's")
|
||||
|
@ -309,14 +309,14 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
condition,
|
||||
region: _,
|
||||
lookups,
|
||||
layouts,
|
||||
variables: _,
|
||||
remainder,
|
||||
}
|
||||
| &Stmt::ExpectFx {
|
||||
condition,
|
||||
region: _,
|
||||
lookups,
|
||||
layouts,
|
||||
variables: _,
|
||||
remainder,
|
||||
} => {
|
||||
self.check_sym_layout(
|
||||
@ -324,8 +324,8 @@ impl<'a, 'r> Ctx<'a, 'r> {
|
||||
Layout::Builtin(Builtin::Bool),
|
||||
UseKind::ExpectCond,
|
||||
);
|
||||
for (sym, lay) in lookups.iter().zip(layouts) {
|
||||
self.check_sym_layout(*sym, *lay, UseKind::ExpectLookup);
|
||||
for sym in lookups.iter() {
|
||||
self.check_sym_exists(*sym);
|
||||
}
|
||||
self.check_stmt(remainder);
|
||||
}
|
||||
|
@ -555,7 +555,13 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||
match &call_type {
|
||||
LowLevel { op, .. } => {
|
||||
let ps = crate::borrow::lowlevel_borrow_signature(self.arena, *op);
|
||||
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||
let b = match op {
|
||||
roc_module::low_level::LowLevel::Dbg => {
|
||||
// NB(dbg-spec-var) second var is the Variable
|
||||
self.add_dec_after_lowlevel(&arguments[..1], ps, b, b_live_vars)
|
||||
}
|
||||
_ => self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars),
|
||||
};
|
||||
|
||||
let v = Expr::Call(crate::ir::Call {
|
||||
call_type,
|
||||
@ -1199,7 +1205,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
} => {
|
||||
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||
|
||||
@ -1207,7 +1213,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
});
|
||||
|
||||
@ -1223,7 +1229,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
} => {
|
||||
let (b, mut b_live_vars) = self.visit_stmt(codegen, remainder);
|
||||
|
||||
@ -1231,7 +1237,7 @@ impl<'a, 'i> Context<'a, 'i> {
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
});
|
||||
|
||||
|
@ -27,14 +27,14 @@ use roc_late_solve::storage::{ExternalModuleStorage, ExternalModuleStorageSnapsh
|
||||
use roc_late_solve::{resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed};
|
||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||
use roc_module::low_level::LowLevel;
|
||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_module::symbol::{IdentId, IdentIds, ModuleId, Symbol};
|
||||
use roc_problem::can::{RuntimeError, ShadowKind};
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_std::RocDec;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{
|
||||
instantiate_rigids, Content, ExhaustiveMark, FlatType, RedundantMark, StorageSubs, Subs,
|
||||
Variable, VariableSubsSlice,
|
||||
instantiate_rigids, storage_copy_var_to, Content, ExhaustiveMark, FlatType, RedundantMark,
|
||||
StorageSubs, Subs, Variable, VariableSubsSlice,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||
@ -1465,6 +1465,9 @@ impl<'a> Specializations<'a> {
|
||||
pub struct Env<'a, 'i> {
|
||||
pub arena: &'a Bump,
|
||||
pub subs: &'i mut Subs,
|
||||
/// [Subs] to write specialized variables of lookups in expects.
|
||||
/// [None] if this module doesn't produce any expects.
|
||||
pub expectation_subs: Option<&'i mut Subs>,
|
||||
pub home: ModuleId,
|
||||
pub ident_ids: &'i mut IdentIds,
|
||||
pub target_info: TargetInfo,
|
||||
@ -1601,6 +1604,9 @@ pub fn cond<'a>(
|
||||
|
||||
pub type Stores<'a> = &'a [(Symbol, Layout<'a>, Expr<'a>)];
|
||||
|
||||
/// The specialized type of a lookup. Represented as a type-variable.
|
||||
pub type LookupType = Variable;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Stmt<'a> {
|
||||
Let(Symbol, Expr<'a>, Layout<'a>, &'a Stmt<'a>),
|
||||
@ -1622,7 +1628,7 @@ pub enum Stmt<'a> {
|
||||
condition: Symbol,
|
||||
region: Region,
|
||||
lookups: &'a [Symbol],
|
||||
layouts: &'a [Layout<'a>],
|
||||
variables: &'a [LookupType],
|
||||
/// what happens after the expect
|
||||
remainder: &'a Stmt<'a>,
|
||||
},
|
||||
@ -1630,7 +1636,7 @@ pub enum Stmt<'a> {
|
||||
condition: Symbol,
|
||||
region: Region,
|
||||
lookups: &'a [Symbol],
|
||||
layouts: &'a [Layout<'a>],
|
||||
variables: &'a [LookupType],
|
||||
/// what happens after the expect
|
||||
remainder: &'a Stmt<'a>,
|
||||
},
|
||||
@ -6570,13 +6576,14 @@ pub fn from_can<'a>(
|
||||
let cond_symbol = env.unique_symbol();
|
||||
|
||||
let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
let mut lookup_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
let mut specialized_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
|
||||
for ExpectLookup {
|
||||
symbol,
|
||||
var,
|
||||
ability_info,
|
||||
} in lookups_in_cond
|
||||
} in lookups_in_cond.iter().copied()
|
||||
{
|
||||
let symbol = match ability_info {
|
||||
Some(specialization_id) => late_resolve_ability_specialization(
|
||||
@ -6587,20 +6594,28 @@ pub fn from_can<'a>(
|
||||
),
|
||||
None => symbol,
|
||||
};
|
||||
let res_layout = layout_cache.from_var(env.arena, var, env.subs);
|
||||
let layout = return_on_layout_error!(env, res_layout, "Expect");
|
||||
if !matches!(layout, Layout::LambdaSet(..)) {
|
||||
|
||||
let expectation_subs = env
|
||||
.expectation_subs
|
||||
.as_deref_mut()
|
||||
.expect("if expects are compiled, their subs should be available");
|
||||
let spec_var = expectation_subs.fresh_unnamed_flex_var();
|
||||
|
||||
if !env.subs.is_function(var) {
|
||||
// Exclude functions from lookups
|
||||
lookups.push(symbol);
|
||||
layouts.push(layout);
|
||||
lookup_variables.push(var);
|
||||
specialized_variables.push(spec_var);
|
||||
}
|
||||
}
|
||||
|
||||
let specialized_variables = specialized_variables.into_bump_slice();
|
||||
|
||||
let mut stmt = Stmt::Expect {
|
||||
condition: cond_symbol,
|
||||
region: loc_condition.region,
|
||||
lookups: lookups.into_bump_slice(),
|
||||
layouts: layouts.into_bump_slice(),
|
||||
variables: specialized_variables,
|
||||
remainder: env.arena.alloc(rest),
|
||||
};
|
||||
|
||||
@ -6614,6 +6629,10 @@ pub fn from_can<'a>(
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
|
||||
// Now that the condition has been specialized, export the specialized types of our
|
||||
// lookups into the expectation subs.
|
||||
store_specialized_expectation_lookups(env, lookup_variables, specialized_variables);
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
@ -6626,13 +6645,14 @@ pub fn from_can<'a>(
|
||||
let cond_symbol = env.unique_symbol();
|
||||
|
||||
let mut lookups = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
let mut layouts = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
let mut lookup_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
let mut specialized_variables = Vec::with_capacity_in(lookups_in_cond.len(), env.arena);
|
||||
|
||||
for ExpectLookup {
|
||||
symbol,
|
||||
var,
|
||||
ability_info,
|
||||
} in lookups_in_cond
|
||||
} in lookups_in_cond.iter().copied()
|
||||
{
|
||||
let symbol = match ability_info {
|
||||
Some(specialization_id) => late_resolve_ability_specialization(
|
||||
@ -6643,20 +6663,28 @@ pub fn from_can<'a>(
|
||||
),
|
||||
None => symbol,
|
||||
};
|
||||
let res_layout = layout_cache.from_var(env.arena, var, env.subs);
|
||||
let layout = return_on_layout_error!(env, res_layout, "Expect");
|
||||
if !matches!(layout, Layout::LambdaSet(..)) {
|
||||
|
||||
let expectation_subs = env
|
||||
.expectation_subs
|
||||
.as_deref_mut()
|
||||
.expect("if expects are compiled, their subs should be available");
|
||||
let spec_var = expectation_subs.fresh_unnamed_flex_var();
|
||||
|
||||
if !env.subs.is_function(var) {
|
||||
// Exclude functions from lookups
|
||||
lookups.push(symbol);
|
||||
layouts.push(layout);
|
||||
lookup_variables.push(var);
|
||||
specialized_variables.push(spec_var);
|
||||
}
|
||||
}
|
||||
|
||||
let specialized_variables = specialized_variables.into_bump_slice();
|
||||
|
||||
let mut stmt = Stmt::ExpectFx {
|
||||
condition: cond_symbol,
|
||||
region: loc_condition.region,
|
||||
lookups: lookups.into_bump_slice(),
|
||||
layouts: layouts.into_bump_slice(),
|
||||
variables: specialized_variables,
|
||||
remainder: env.arena.alloc(rest),
|
||||
};
|
||||
|
||||
@ -6670,6 +6698,8 @@ pub fn from_can<'a>(
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
|
||||
store_specialized_expectation_lookups(env, lookup_variables, specialized_variables);
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
@ -6681,12 +6711,23 @@ pub fn from_can<'a>(
|
||||
} => {
|
||||
let rest = from_can(env, variable, loc_continuation.value, procs, layout_cache);
|
||||
|
||||
let spec_var = env
|
||||
.expectation_subs
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.fresh_unnamed_flex_var();
|
||||
// HACK(dbg-spec-var): pass the specialized type variable along injected into a fake symbol
|
||||
let dbg_spec_var_symbol = Symbol::new(ModuleId::ATTR, unsafe {
|
||||
IdentId::from_index(spec_var.index())
|
||||
});
|
||||
|
||||
// TODO: need to store the specialized variable of this dbg in the expectation_subs
|
||||
let call = crate::ir::Call {
|
||||
call_type: CallType::LowLevel {
|
||||
op: LowLevel::Dbg,
|
||||
update_mode: env.next_update_mode_id(),
|
||||
},
|
||||
arguments: env.arena.alloc([dbg_symbol]),
|
||||
arguments: env.arena.alloc([dbg_symbol, dbg_spec_var_symbol]),
|
||||
};
|
||||
|
||||
let dbg_layout = layout_cache
|
||||
@ -6714,6 +6755,10 @@ pub fn from_can<'a>(
|
||||
);
|
||||
}
|
||||
|
||||
// Now that the dbg value has been specialized, export its specialized type into the
|
||||
// expectations subs.
|
||||
store_specialized_expectation_lookups(env, [variable], &[spec_var]);
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
@ -6752,6 +6797,21 @@ pub fn from_can<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn store_specialized_expectation_lookups(
|
||||
env: &mut Env,
|
||||
lookup_variables: impl IntoIterator<Item = Variable>,
|
||||
specialized_variables: &[Variable],
|
||||
) {
|
||||
let subs = &env.subs;
|
||||
let expectation_subs = env.expectation_subs.as_deref_mut().unwrap();
|
||||
for (lookup_var, stored_var) in lookup_variables.into_iter().zip(specialized_variables) {
|
||||
let stored_specialized_var =
|
||||
storage_copy_var_to(&mut Default::default(), subs, expectation_subs, lookup_var);
|
||||
let stored_specialized_desc = expectation_subs.get(stored_specialized_var);
|
||||
expectation_subs.union(*stored_var, stored_specialized_var, stored_specialized_desc);
|
||||
}
|
||||
}
|
||||
|
||||
fn to_opt_branches<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
@ -7082,7 +7142,7 @@ fn substitute_in_stmt_help<'a>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let new_remainder =
|
||||
@ -7097,7 +7157,7 @@ fn substitute_in_stmt_help<'a>(
|
||||
condition: substitute(subs, *condition).unwrap_or(*condition),
|
||||
region: *region,
|
||||
lookups: new_lookups.into_bump_slice(),
|
||||
layouts,
|
||||
variables,
|
||||
remainder: new_remainder,
|
||||
};
|
||||
|
||||
@ -7108,7 +7168,7 @@ fn substitute_in_stmt_help<'a>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let new_remainder =
|
||||
@ -7123,7 +7183,7 @@ fn substitute_in_stmt_help<'a>(
|
||||
condition: substitute(subs, *condition).unwrap_or(*condition),
|
||||
region: *region,
|
||||
lookups: new_lookups.into_bump_slice(),
|
||||
layouts,
|
||||
variables,
|
||||
remainder: new_remainder,
|
||||
};
|
||||
|
||||
@ -8234,49 +8294,71 @@ fn specialize_symbol<'a>(
|
||||
Err(e) => return_on_layout_error_help!(env, e, "specialize_symbol"),
|
||||
};
|
||||
|
||||
if procs.is_imported_module_thunk(original) {
|
||||
let layout = match raw {
|
||||
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
Layout::LambdaSet(lambda_set)
|
||||
}
|
||||
};
|
||||
match raw {
|
||||
RawFunctionLayout::Function(_, lambda_set, _)
|
||||
if !procs.is_imported_module_thunk(original) =>
|
||||
{
|
||||
let lambda_name =
|
||||
find_lambda_name(env, layout_cache, lambda_set, original, &[]);
|
||||
|
||||
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
debug_assert!(
|
||||
lambda_name.no_captures(),
|
||||
"imported functions are top-level and should never capture"
|
||||
);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
LambdaName::no_niche(original),
|
||||
top_level,
|
||||
layout_cache,
|
||||
);
|
||||
let function_ptr_layout = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw,
|
||||
lambda_name.captures_niche(),
|
||||
);
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
lambda_name,
|
||||
function_ptr_layout,
|
||||
layout_cache,
|
||||
);
|
||||
|
||||
force_thunk(env, original, layout, assign_to, env.arena.alloc(result))
|
||||
} else {
|
||||
// Imported symbol, so it must have no captures niche (since
|
||||
// top-levels can't capture)
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
LambdaName::no_niche(original),
|
||||
top_level,
|
||||
layout_cache,
|
||||
);
|
||||
construct_closure_data(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
lambda_set,
|
||||
lambda_name,
|
||||
&[],
|
||||
assign_to,
|
||||
env.arena.alloc(result),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// This is an imported ZAT that returns either a value, or the closure
|
||||
// data for a lambda set.
|
||||
let layout = match raw {
|
||||
RawFunctionLayout::ZeroArgumentThunk(layout) => layout,
|
||||
RawFunctionLayout::Function(_, lambda_set, _) => {
|
||||
Layout::LambdaSet(lambda_set)
|
||||
}
|
||||
};
|
||||
|
||||
let_empty_struct(assign_to, env.arena.alloc(result))
|
||||
let raw = RawFunctionLayout::ZeroArgumentThunk(layout);
|
||||
let top_level = ProcLayout::from_raw(
|
||||
env.arena,
|
||||
&layout_cache.interner,
|
||||
raw,
|
||||
CapturesNiche::no_niche(),
|
||||
);
|
||||
|
||||
procs.insert_passed_by_name(
|
||||
env,
|
||||
arg_var,
|
||||
LambdaName::no_niche(original),
|
||||
top_level,
|
||||
layout_cache,
|
||||
);
|
||||
|
||||
force_thunk(env, original, layout, assign_to, env.arena.alloc(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2890,7 +2890,6 @@ impl<'a> Builtin<'a> {
|
||||
use FloatWidth::*;
|
||||
|
||||
match float_width {
|
||||
F128 => alloc.text("Float128"),
|
||||
F64 => alloc.text("Float64"),
|
||||
F32 => alloc.text("Float32"),
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ fn function_s<'a, 'i>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let continuation: &Stmt = remainder;
|
||||
@ -208,7 +208,7 @@ fn function_s<'a, 'i>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: new_continuation,
|
||||
};
|
||||
|
||||
@ -220,7 +220,7 @@ fn function_s<'a, 'i>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let continuation: &Stmt = remainder;
|
||||
@ -233,7 +233,7 @@ fn function_s<'a, 'i>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: new_continuation,
|
||||
};
|
||||
|
||||
@ -442,7 +442,7 @@ fn function_d_main<'a, 'i>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let (b, found) = function_d_main(env, x, c, remainder);
|
||||
@ -452,7 +452,7 @@ fn function_d_main<'a, 'i>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
@ -464,7 +464,7 @@ fn function_d_main<'a, 'i>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
@ -475,7 +475,7 @@ fn function_d_main<'a, 'i>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let (b, found) = function_d_main(env, x, c, remainder);
|
||||
@ -485,7 +485,7 @@ fn function_d_main<'a, 'i>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
@ -497,7 +497,7 @@ fn function_d_main<'a, 'i>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
@ -660,7 +660,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let b = function_r(env, remainder);
|
||||
@ -669,7 +669,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
@ -680,7 +680,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => {
|
||||
let b = function_r(env, remainder);
|
||||
@ -689,7 +689,7 @@ fn function_r<'a, 'i>(env: &mut Env<'a, 'i>, stmt: &'a Stmt<'a>) -> &'a Stmt<'a>
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: b,
|
||||
};
|
||||
|
||||
|
@ -253,7 +253,7 @@ fn insert_jumps<'a>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => match insert_jumps(
|
||||
arena,
|
||||
@ -267,7 +267,7 @@ fn insert_jumps<'a>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: cont,
|
||||
})),
|
||||
None => None,
|
||||
@ -277,7 +277,7 @@ fn insert_jumps<'a>(
|
||||
condition,
|
||||
region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder,
|
||||
} => match insert_jumps(
|
||||
arena,
|
||||
@ -291,7 +291,7 @@ fn insert_jumps<'a>(
|
||||
condition: *condition,
|
||||
region: *region,
|
||||
lookups,
|
||||
layouts,
|
||||
variables,
|
||||
remainder: cont,
|
||||
})),
|
||||
None => None,
|
||||
|
@ -262,6 +262,7 @@ mod solve_expr {
|
||||
|
||||
#[derive(Default)]
|
||||
struct InferOptions {
|
||||
print_can_decls: bool,
|
||||
print_only_under_alias: bool,
|
||||
allow_errors: bool,
|
||||
}
|
||||
@ -302,7 +303,20 @@ mod solve_expr {
|
||||
let queries = parse_queries(&src);
|
||||
assert!(!queries.is_empty(), "No queries provided!");
|
||||
|
||||
let mut solved_queries = Vec::with_capacity(queries.len());
|
||||
let mut output_parts = Vec::with_capacity(queries.len() + 2);
|
||||
|
||||
if options.print_can_decls {
|
||||
use roc_can::debug::{pretty_print_declarations, PPCtx};
|
||||
let ctx = PPCtx {
|
||||
home,
|
||||
interns: &interns,
|
||||
print_lambda_names: true,
|
||||
};
|
||||
let pretty_decls = pretty_print_declarations(&ctx, &decls);
|
||||
output_parts.push(pretty_decls);
|
||||
output_parts.push("\n".to_owned());
|
||||
}
|
||||
|
||||
for TypeQuery(region) in queries.into_iter() {
|
||||
let start = region.start().offset;
|
||||
let end = region.end().offset;
|
||||
@ -340,12 +354,12 @@ mod solve_expr {
|
||||
}
|
||||
};
|
||||
|
||||
solved_queries.push(elaborated);
|
||||
output_parts.push(elaborated);
|
||||
}
|
||||
|
||||
let pretty_solved_queries = solved_queries.join("\n");
|
||||
let pretty_output = output_parts.join("\n");
|
||||
|
||||
expected(&pretty_solved_queries);
|
||||
expected(&pretty_output);
|
||||
}
|
||||
|
||||
macro_rules! infer_queries {
|
||||
@ -6720,9 +6734,9 @@ mod solve_expr {
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||
Id#id(3) : a -[[] + a:id(3):1]-> ({} -[[] + a:id(3):2]-> a) | a has Id
|
||||
alias : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||
alias : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||
"#
|
||||
print_only_under_alias: true
|
||||
)
|
||||
@ -6751,8 +6765,8 @@ mod solve_expr {
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||
it : {} -[[8(8)]]-> {}
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||
it : {} -[[8]]-> {}
|
||||
"#
|
||||
print_only_under_alias: true
|
||||
)
|
||||
@ -6782,8 +6796,8 @@ mod solve_expr {
|
||||
"#
|
||||
),
|
||||
@r#"
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8(8)]]-> {})
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||
A#id(5) : {} -[[id(5)]]-> ({} -[[8]]-> {})
|
||||
"#
|
||||
print_only_under_alias: true
|
||||
)
|
||||
@ -6903,7 +6917,7 @@ mod solve_expr {
|
||||
#^^^^^^^^^^^^^^^^^^^^^^{-1}
|
||||
"#
|
||||
),
|
||||
@r#"[\{} -> {}, \{} -> {}] : List ({}* -[[1(1), 2(2)]]-> {})"#
|
||||
@r###"[\{} -> {}, \{} -> {}] : List ({}* -[[1, 2]]-> {})"###
|
||||
)
|
||||
}
|
||||
|
||||
@ -7078,7 +7092,7 @@ mod solve_expr {
|
||||
#^^^{-1}
|
||||
"#
|
||||
),
|
||||
@r#"fun : {} -[[thunk(9) (({} -[[15(15)]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13(13) Str]]-> Str)), thunk(9) (({} -[[14(14)]]-> Str)) (Str -[[f(3)]]-> ({} -[[11(11)]]-> Str))]]-> Str"#
|
||||
@r#"fun : {} -[[thunk(9) (({} -[[15]]-> { s1 : Str })) ({ s1 : Str } -[[g(4)]]-> ({} -[[13 Str]]-> Str)), thunk(9) (({} -[[14]]-> Str)) (Str -[[f(3)]]-> ({} -[[11]]-> Str))]]-> Str"#
|
||||
print_only_under_alias: true
|
||||
);
|
||||
}
|
||||
@ -7323,9 +7337,9 @@ mod solve_expr {
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
Fo#f(7) : Fo, b -[[f(7)]]-> ({} -[[13(13) b]]-> ({} -[[] + b:g(4):2]-> {})) | b has G
|
||||
Go#g(8) : Go -[[g(8)]]-> ({} -[[14(14)]]-> {})
|
||||
Fo#f(7) : Fo, Go -[[f(7)]]-> ({} -[[13(13) Go]]-> ({} -[[14(14)]]-> {}))
|
||||
Fo#f(7) : Fo, b -[[f(7)]]-> ({} -[[13 b]]-> ({} -[[] + b:g(4):2]-> {})) | b has G
|
||||
Go#g(8) : Go -[[g(8)]]-> ({} -[[14]]-> {})
|
||||
Fo#f(7) : Fo, Go -[[f(7)]]-> ({} -[[13 Go]]-> ({} -[[14]]-> {}))
|
||||
"###
|
||||
);
|
||||
}
|
||||
@ -7692,7 +7706,7 @@ mod solve_expr {
|
||||
@r###"
|
||||
const : Str -[[const(2)]]-> (Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str)
|
||||
compose : (Str -a-> Str), (Str -[[]]-> Str) -[[compose(1)]]-> (Str -a-> Str)
|
||||
\c1, c2 -> compose c1 c2 : (Str -a-> Str), (Str -[[]]-> Str) -[[11(11)]]-> (Str -a-> Str)
|
||||
\c1, c2 -> compose c1 c2 : (Str -a-> Str), (Str -[[]]-> Str) -[[11]]-> (Str -a-> Str)
|
||||
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
|
||||
res : Str -[[closCompose(7) (Str -a-> Str) (Str -[[]]-> Str), closConst(10) Str] as a]-> Str
|
||||
"###
|
||||
@ -8077,7 +8091,7 @@ mod solve_expr {
|
||||
# ^^^^^^^^^^^^^^
|
||||
"#
|
||||
),
|
||||
@"N#Decode.decoder(3) : List U8, fmt -[[7(7)]]-> { rest : List U8, result : [Err [TooShort], Ok U8] } | fmt has DecoderFormatting"
|
||||
@"N#Decode.decoder(3) : List U8, fmt -[[7]]-> { rest : List U8, result : [Err [TooShort], Ok U8] } | fmt has DecoderFormatting"
|
||||
print_only_under_alias: true
|
||||
);
|
||||
}
|
||||
@ -8360,7 +8374,7 @@ mod solve_expr {
|
||||
),
|
||||
@r###"
|
||||
isEqQ : ({} -[[]]-> Str), ({} -[[]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||
isEqQ : ({} -[[6(6), 7(7)]]-> Str), ({} -[[6(6), 7(7)]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||
isEqQ : ({} -[[6, 7]]-> Str), ({} -[[6, 7]]-> Str) -[[isEqQ(2)]]-> [False, True]
|
||||
"###
|
||||
print_only_under_alias: true
|
||||
);
|
||||
@ -8456,4 +8470,82 @@ mod solve_expr {
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disjoint_nested_lambdas_result_in_disjoint_parents_issue_4712() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Parser a : {} -> a
|
||||
|
||||
v1 : {}
|
||||
v1 = {}
|
||||
|
||||
v2 : Str
|
||||
v2 = ""
|
||||
|
||||
apply : Parser (a -> Str), a -> Parser Str
|
||||
apply = \fnParser, valParser ->
|
||||
\{} ->
|
||||
(fnParser {}) (valParser)
|
||||
|
||||
map : a, (a -> Str) -> Parser Str
|
||||
map = \simpleParser, transform ->
|
||||
apply (\{} -> transform) simpleParser
|
||||
|
||||
parseInput = \{} ->
|
||||
when [ map v1 (\{} -> ""), map v2 (\s -> s) ] is
|
||||
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
_ -> ""
|
||||
|
||||
main = parseInput {} == ""
|
||||
"#
|
||||
),
|
||||
@r###"
|
||||
v1 = {}
|
||||
|
||||
v2 = ""
|
||||
|
||||
apply = \fnParser, valParser-> \{} -[9]-> (fnParser {}) valParser
|
||||
|
||||
map = \simpleParser, transform-> apply \{} -[12]-> transform simpleParser
|
||||
|
||||
parseInput =
|
||||
\{}->
|
||||
when [
|
||||
map v1 \{} -[13]-> "",
|
||||
map v2 \s -[14]-> s,
|
||||
] is
|
||||
_ -> ""
|
||||
|
||||
main = Bool.isEq (parseInput {}) ""
|
||||
|
||||
|
||||
[ map v1 (\{} -> ""), map v2 (\s -> s) ] : List (({} -[[9 (({} -[[12 (Str -[[14]]-> Str)]]-> (Str -[[14]]-> Str))) Str, 9 (({} -[[12 ({} -[[13]]-> Str)]]-> ({} -[[13]]-> Str))) {}]]-> Str))
|
||||
"###
|
||||
print_only_under_alias: true
|
||||
print_can_decls: true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constrain_dbg_flex_var() {
|
||||
infer_queries!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
polyDbg = \x ->
|
||||
#^^^^^^^{-1}
|
||||
dbg x
|
||||
x
|
||||
|
||||
main = polyDbg ""
|
||||
"#
|
||||
),
|
||||
@"polyDbg : a -[[polyDbg(1)]]-> a"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -103,9 +103,9 @@ fn list() {
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[custom(3)]]
|
||||
#Derived.decoder_list =
|
||||
Decode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Decode.decodeWith #Derived.bytes (Decode.list Decode.decoder) #Derived.fmt
|
||||
decodeWith #Derived.bytes (list decoder) #Derived.fmt
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -121,21 +121,18 @@ fn record_2_fields() {
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[custom(22)]]
|
||||
#Derived.decoder_{first,second} =
|
||||
Decode.custom
|
||||
custom
|
||||
\#Derived.bytes3, #Derived.fmt3 ->
|
||||
Decode.decodeWith
|
||||
decodeWith
|
||||
#Derived.bytes3
|
||||
(Decode.record
|
||||
(record
|
||||
{ second: Err NoField, first: Err NoField }
|
||||
\#Derived.stateRecord2, #Derived.field ->
|
||||
when #Derived.field is
|
||||
"first" ->
|
||||
Keep (Decode.custom
|
||||
Keep (custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
when Decode.decodeWith
|
||||
#Derived.bytes
|
||||
Decode.decoder
|
||||
#Derived.fmt is
|
||||
when decodeWith #Derived.bytes decoder #Derived.fmt is
|
||||
#Derived.rec ->
|
||||
{
|
||||
result: when #Derived.rec.result is
|
||||
@ -145,12 +142,9 @@ fn record_2_fields() {
|
||||
rest: #Derived.rec.rest
|
||||
})
|
||||
"second" ->
|
||||
Keep (Decode.custom
|
||||
Keep (custom
|
||||
\#Derived.bytes2, #Derived.fmt2 ->
|
||||
when Decode.decodeWith
|
||||
#Derived.bytes2
|
||||
Decode.decoder
|
||||
#Derived.fmt2 is
|
||||
when decodeWith #Derived.bytes2 decoder #Derived.fmt2 is
|
||||
#Derived.rec2 ->
|
||||
{
|
||||
result: when #Derived.rec2.result is
|
||||
|
@ -187,9 +187,9 @@ fn empty_record() {
|
||||
# @<2>: [[custom(2) {}]]
|
||||
#Derived.toEncoder_{} =
|
||||
\#Derived.rcd ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith #Derived.bytes (Encode.record []) #Derived.fmt
|
||||
appendWith #Derived.bytes (record []) #Derived.fmt
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -207,9 +207,9 @@ fn zero_field_record() {
|
||||
# @<2>: [[custom(2) {}]]
|
||||
#Derived.toEncoder_{} =
|
||||
\#Derived.rcd ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith #Derived.bytes (Encode.record []) #Derived.fmt
|
||||
appendWith #Derived.bytes (record []) #Derived.fmt
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -227,11 +227,11 @@ fn one_field_record() {
|
||||
# @<2>: [[custom(2) { a : val }]] | val has Encoding
|
||||
#Derived.toEncoder_{a} =
|
||||
\#Derived.rcd ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(Encode.record [{ value: Encode.toEncoder #Derived.rcd.a, key: "a" }])
|
||||
(record [{ value: toEncoder #Derived.rcd.a, key: "a" }])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
@ -250,14 +250,14 @@ fn two_field_record() {
|
||||
# @<2>: [[custom(2) { a : val, b : val1 }]] | val has Encoding, val1 has Encoding
|
||||
#Derived.toEncoder_{a,b} =
|
||||
\#Derived.rcd ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(Encode.record
|
||||
(record
|
||||
[
|
||||
{ value: Encode.toEncoder #Derived.rcd.a, key: "a" },
|
||||
{ value: Encode.toEncoder #Derived.rcd.b, key: "b" },
|
||||
{ value: toEncoder #Derived.rcd.a, key: "a" },
|
||||
{ value: toEncoder #Derived.rcd.b, key: "b" },
|
||||
])
|
||||
#Derived.fmt
|
||||
"###
|
||||
@ -290,12 +290,12 @@ fn tag_one_label_zero_args() {
|
||||
# @<2>: [[custom(2) [A]]]
|
||||
#Derived.toEncoder_[A 0] =
|
||||
\#Derived.tag ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is
|
||||
A -> Encode.tag "A" [])
|
||||
A -> tag "A" [])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
@ -314,18 +314,13 @@ fn tag_one_label_two_args() {
|
||||
# @<2>: [[custom(4) [A val val1]]] | val has Encoding, val1 has Encoding
|
||||
#Derived.toEncoder_[A 2] =
|
||||
\#Derived.tag ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is
|
||||
A #Derived.2 #Derived.3 ->
|
||||
Encode.tag
|
||||
"A"
|
||||
[
|
||||
Encode.toEncoder #Derived.2,
|
||||
Encode.toEncoder #Derived.3,
|
||||
])
|
||||
tag "A" [toEncoder #Derived.2, toEncoder #Derived.3])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
@ -339,30 +334,30 @@ fn tag_two_labels() {
|
||||
v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]),
|
||||
|golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [A U8 Str U16, B Str]
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[A 3,B 1](0)]]
|
||||
# @<2>: [[custom(6) [A val val1 val1, B val1]]] | val has Encoding, val1 has Encoding
|
||||
#Derived.toEncoder_[A 3,B 1] =
|
||||
\#Derived.tag ->
|
||||
Encode.custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is
|
||||
A #Derived.2 #Derived.3 #Derived.4 ->
|
||||
Encode.tag
|
||||
"A"
|
||||
[
|
||||
Encode.toEncoder #Derived.2,
|
||||
Encode.toEncoder #Derived.3,
|
||||
Encode.toEncoder #Derived.4,
|
||||
]
|
||||
B #Derived.5 -> Encode.tag "B" [Encode.toEncoder #Derived.5])
|
||||
#Derived.fmt
|
||||
"###
|
||||
# derived for [A U8 Str U16, B Str]
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[A 3,B 1](0)]]
|
||||
# @<2>: [[custom(6) [A val val1 val1, B val1]]] | val has Encoding, val1 has Encoding
|
||||
#Derived.toEncoder_[A 3,B 1] =
|
||||
\#Derived.tag ->
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is
|
||||
A #Derived.2 #Derived.3 #Derived.4 ->
|
||||
tag
|
||||
"A"
|
||||
[
|
||||
toEncoder #Derived.2,
|
||||
toEncoder #Derived.3,
|
||||
toEncoder #Derived.4,
|
||||
]
|
||||
B #Derived.5 -> tag "B" [toEncoder #Derived.5])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -375,29 +370,24 @@ fn recursive_tag_union() {
|
||||
v!([Nil, Cons v!(U8) v!(^lst) ] as lst),
|
||||
|golden| {
|
||||
assert_snapshot!(golden, @r###"
|
||||
# derived for [Cons U8 $rec, Nil] as $rec
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[Cons 2,Nil 0](0)]]
|
||||
# @<2>: [[custom(4) [Cons val val1, Nil]]] | val has Encoding, val1 has Encoding
|
||||
#Derived.toEncoder_[Cons 2,Nil 0] =
|
||||
\#Derived.tag ->
|
||||
Encode.custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is
|
||||
Cons #Derived.2 #Derived.3 ->
|
||||
Encode.tag
|
||||
"Cons"
|
||||
[
|
||||
Encode.toEncoder #Derived.2,
|
||||
Encode.toEncoder #Derived.3,
|
||||
]
|
||||
Nil -> Encode.tag "Nil" [])
|
||||
#Derived.fmt
|
||||
"###
|
||||
# derived for [Cons U8 $rec, Nil] as $rec
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[toEncoder_[Cons 2,Nil 0](0)]]
|
||||
# @<2>: [[custom(4) [Cons val val1, Nil]]] | val has Encoding, val1 has Encoding
|
||||
#Derived.toEncoder_[Cons 2,Nil 0] =
|
||||
\#Derived.tag ->
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(when #Derived.tag is
|
||||
Cons #Derived.2 #Derived.3 ->
|
||||
tag "Cons" [toEncoder #Derived.2, toEncoder #Derived.3]
|
||||
Nil -> tag "Nil" [])
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -415,13 +405,11 @@ fn list() {
|
||||
# @<2>: [[custom(4) (List val)]] | val has Encoding
|
||||
#Derived.toEncoder_list =
|
||||
\#Derived.lst ->
|
||||
Encode.custom
|
||||
custom
|
||||
\#Derived.bytes, #Derived.fmt ->
|
||||
Encode.appendWith
|
||||
appendWith
|
||||
#Derived.bytes
|
||||
(Encode.list
|
||||
#Derived.lst
|
||||
\#Derived.elem -> Encode.toEncoder #Derived.elem)
|
||||
(list #Derived.lst \#Derived.elem -> toEncoder #Derived.elem)
|
||||
#Derived.fmt
|
||||
"###
|
||||
)
|
||||
|
@ -178,7 +178,7 @@ fn one_field_record() {
|
||||
# Specialization lambda sets:
|
||||
# @<1>: [[hash_{a}(0)]]
|
||||
#Derived.hash_{a} =
|
||||
\#Derived.hasher, #Derived.rcd -> Hash.hash #Derived.hasher #Derived.rcd.a
|
||||
\#Derived.hasher, #Derived.rcd -> hash #Derived.hasher #Derived.rcd.a
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -195,7 +195,7 @@ fn two_field_record() {
|
||||
# @<1>: [[hash_{a,b}(0)]]
|
||||
#Derived.hash_{a,b} =
|
||||
\#Derived.hasher, #Derived.rcd ->
|
||||
Hash.hash (Hash.hash #Derived.hasher #Derived.rcd.a) #Derived.rcd.b
|
||||
hash (hash #Derived.hasher #Derived.rcd.a) #Derived.rcd.b
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -227,7 +227,7 @@ fn tag_one_label_newtype() {
|
||||
# @<1>: [[hash_[A 2](0)]]
|
||||
#Derived.hash_[A 2] =
|
||||
\#Derived.hasher, A #Derived.2 #Derived.3 ->
|
||||
Hash.hash (Hash.hash #Derived.hasher #Derived.2) #Derived.3
|
||||
hash (hash #Derived.hasher #Derived.2) #Derived.3
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -246,12 +246,10 @@ fn tag_two_labels() {
|
||||
\#Derived.hasher, #Derived.union ->
|
||||
when #Derived.union is
|
||||
A #Derived.3 #Derived.4 #Derived.5 ->
|
||||
Hash.hash
|
||||
(Hash.hash
|
||||
(Hash.hash (Hash.addU8 #Derived.hasher 0) #Derived.3)
|
||||
#Derived.4)
|
||||
hash
|
||||
(hash (hash (addU8 #Derived.hasher 0) #Derived.3) #Derived.4)
|
||||
#Derived.5
|
||||
B #Derived.6 -> Hash.hash (Hash.addU8 #Derived.hasher 1) #Derived.6
|
||||
B #Derived.6 -> hash (addU8 #Derived.hasher 1) #Derived.6
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -269,8 +267,8 @@ fn tag_two_labels_no_payloads() {
|
||||
#Derived.hash_[A 0,B 0] =
|
||||
\#Derived.hasher, #Derived.union ->
|
||||
when #Derived.union is
|
||||
A -> Hash.addU8 #Derived.hasher 0
|
||||
B -> Hash.addU8 #Derived.hasher 1
|
||||
A -> addU8 #Derived.hasher 0
|
||||
B -> addU8 #Derived.hasher 1
|
||||
"###
|
||||
)
|
||||
})
|
||||
@ -289,10 +287,8 @@ fn recursive_tag_union() {
|
||||
\#Derived.hasher, #Derived.union ->
|
||||
when #Derived.union is
|
||||
Cons #Derived.3 #Derived.4 ->
|
||||
Hash.hash
|
||||
(Hash.hash (Hash.addU8 #Derived.hasher 0) #Derived.3)
|
||||
#Derived.4
|
||||
Nil -> Hash.addU8 #Derived.hasher 1
|
||||
hash (hash (addU8 #Derived.hasher 0) #Derived.3) #Derived.4
|
||||
Nil -> addU8 #Derived.hasher 1
|
||||
"###
|
||||
)
|
||||
})
|
||||
|
@ -5,5 +5,4 @@ mod encoding;
|
||||
mod eq;
|
||||
mod hash;
|
||||
|
||||
mod pretty_print;
|
||||
mod util;
|
||||
|
@ -5,10 +5,10 @@ use bumpalo::Bump;
|
||||
use roc_packaging::cache::RocCacheDir;
|
||||
use ven_pretty::DocAllocator;
|
||||
|
||||
use crate::pretty_print::{pretty_print_def, Ctx};
|
||||
use roc_can::{
|
||||
abilities::{AbilitiesStore, SpecializationLambdaSets},
|
||||
constraint::Constraints,
|
||||
debug::{pretty_print_def, PPCtx},
|
||||
def::Def,
|
||||
expr::Declarations,
|
||||
module::{
|
||||
@ -529,8 +529,12 @@ where
|
||||
interns.all_ident_ids.insert(DERIVED_MODULE, ident_ids);
|
||||
DERIVED_MODULE.register_debug_idents(interns.all_ident_ids.get(&DERIVED_MODULE).unwrap());
|
||||
|
||||
let ctx = Ctx { interns: &interns };
|
||||
let derived_program = pretty_print_def(&ctx, &derived_def);
|
||||
let pp_ctx = PPCtx {
|
||||
interns: &interns,
|
||||
print_lambda_names: false,
|
||||
home: builtin_module,
|
||||
};
|
||||
let derived_program = pretty_print_def(&pp_ctx, &derived_def);
|
||||
|
||||
check_derived_typechecks_and_golden(
|
||||
derived_def,
|
||||
|
@ -4107,3 +4107,41 @@ fn issue_4349() {
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn issue_4712() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
Parser a : {} -> a
|
||||
|
||||
v1 : {}
|
||||
v1 = {}
|
||||
|
||||
v2 : Str
|
||||
v2 = "cd"
|
||||
|
||||
apply : Parser (a -> Str), a -> Parser Str
|
||||
apply = \fnParser, valParser ->
|
||||
\{} ->
|
||||
(fnParser {}) (valParser)
|
||||
|
||||
map : a, (a -> Str) -> Parser Str
|
||||
map = \simpleParser, transform ->
|
||||
apply (\{} -> transform) simpleParser
|
||||
|
||||
gen = \{} ->
|
||||
[ map v1 (\{} -> "ab"), map v2 (\s -> s) ]
|
||||
|> List.map (\f -> f {})
|
||||
|> Str.joinWith ","
|
||||
|
||||
main = gen {}
|
||||
"#
|
||||
),
|
||||
RocStr::from("ab,cd"),
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
@ -2083,3 +2083,53 @@ fn unify_types_with_fixed_fixpoints_outside_fixing_region() {
|
||||
RocStr
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn lambda_set_with_imported_toplevels_issue_4733() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
fn = \s ->
|
||||
instr = if s == "*" then (Op Num.mul) else (Op Num.add)
|
||||
|
||||
Op op = instr
|
||||
|
||||
\a -> op a a
|
||||
|
||||
main = ((fn "*") 3) * ((fn "+") 5)
|
||||
"#
|
||||
),
|
||||
90,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
|
||||
fn non_unary_union_with_lambda_set_with_imported_toplevels_issue_4733() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app "test" provides [main] to "./platform"
|
||||
|
||||
fn = \s ->
|
||||
instr =
|
||||
if s == "*" then (Op Num.mul)
|
||||
else if s == "+" then (Op Num.add)
|
||||
else Noop
|
||||
|
||||
when instr is
|
||||
Op op -> (\a -> op a a)
|
||||
_ -> (\a -> a)
|
||||
|
||||
|
||||
main = ((fn "*") 3) * ((fn "+") 5)
|
||||
"#
|
||||
),
|
||||
90,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
316
crates/compiler/test_mono/generated/issue_4749.txt
Normal file
316
crates/compiler/test_mono/generated/issue_4749.txt
Normal file
@ -0,0 +1,316 @@
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
dec #Attr.3;
|
||||
dec #Attr.2;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.31 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.31;
|
||||
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.38 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.38;
|
||||
|
||||
procedure Bool.12 (#Attr.2, #Attr.3):
|
||||
let Bool.30 : Int1 = lowlevel NotEq #Attr.2 #Attr.3;
|
||||
ret Bool.30;
|
||||
|
||||
procedure Bool.7 (Bool.19, Bool.20):
|
||||
let Bool.29 : Int1 = CallByName Bool.12 Bool.19 Bool.20;
|
||||
ret Bool.29;
|
||||
|
||||
procedure Decode.23 (Decode.94):
|
||||
ret Decode.94;
|
||||
|
||||
procedure Decode.24 (Decode.95, Decode.114, Decode.97):
|
||||
let Decode.127 : {List U8, [C {}, C Str]} = CallByName Json.293 Decode.95 Decode.97;
|
||||
ret Decode.127;
|
||||
|
||||
procedure Decode.25 (Decode.98, Decode.99):
|
||||
let Decode.126 : {} = CallByName Json.41;
|
||||
let Decode.125 : {List U8, [C {}, C Str]} = CallByName Decode.24 Decode.98 Decode.126 Decode.99;
|
||||
ret Decode.125;
|
||||
|
||||
procedure Decode.26 (Decode.100, Decode.101):
|
||||
let Decode.115 : {List U8, [C {}, C Str]} = CallByName Decode.25 Decode.100 Decode.101;
|
||||
let Decode.103 : List U8 = StructAtIndex 0 Decode.115;
|
||||
inc Decode.103;
|
||||
let Decode.102 : [C {}, C Str] = StructAtIndex 1 Decode.115;
|
||||
inc Decode.102;
|
||||
dec Decode.115;
|
||||
let Decode.118 : Int1 = CallByName List.1 Decode.103;
|
||||
if Decode.118 then
|
||||
dec Decode.103;
|
||||
let Decode.122 : U8 = 1i64;
|
||||
let Decode.123 : U8 = GetTagId Decode.102;
|
||||
let Decode.124 : Int1 = lowlevel Eq Decode.122 Decode.123;
|
||||
if Decode.124 then
|
||||
let Decode.104 : Str = UnionAtIndex (Id 1) (Index 0) Decode.102;
|
||||
inc Decode.104;
|
||||
dec Decode.102;
|
||||
let Decode.119 : [C [C List U8, C ], C Str] = TagId(1) Decode.104;
|
||||
ret Decode.119;
|
||||
else
|
||||
dec Decode.102;
|
||||
let Decode.121 : [C List U8, C ] = TagId(1) ;
|
||||
let Decode.120 : [C [C List U8, C ], C Str] = TagId(0) Decode.121;
|
||||
ret Decode.120;
|
||||
else
|
||||
dec Decode.102;
|
||||
let Decode.117 : [C List U8, C ] = TagId(0) Decode.103;
|
||||
let Decode.116 : [C [C List U8, C ], C Str] = TagId(0) Decode.117;
|
||||
ret Decode.116;
|
||||
|
||||
procedure Json.139 (Json.450, Json.451):
|
||||
joinpoint Json.421 Json.418 Json.138:
|
||||
let Json.141 : List U8 = StructAtIndex 0 Json.418;
|
||||
inc Json.141;
|
||||
let Json.140 : List U8 = StructAtIndex 1 Json.418;
|
||||
inc Json.140;
|
||||
dec Json.418;
|
||||
let Json.422 : [C {}, C U8] = CallByName List.9 Json.141;
|
||||
let Json.436 : U8 = 1i64;
|
||||
let Json.437 : U8 = GetTagId Json.422;
|
||||
let Json.438 : Int1 = lowlevel Eq Json.436 Json.437;
|
||||
if Json.438 then
|
||||
let Json.142 : U8 = UnionAtIndex (Id 1) (Index 0) Json.422;
|
||||
let Json.424 : Int1 = CallByName Json.283 Json.142;
|
||||
if Json.424 then
|
||||
let Json.434 : U64 = 1i64;
|
||||
let Json.430 : {List U8, List U8} = CallByName List.52 Json.141 Json.434;
|
||||
let Json.431 : {} = Struct {};
|
||||
let Json.428 : List U8 = CallByName Json.143 Json.430;
|
||||
let Json.429 : List U8 = CallByName List.4 Json.140 Json.142;
|
||||
let Json.426 : {List U8, List U8} = Struct {Json.428, Json.429};
|
||||
jump Json.421 Json.426 Json.138;
|
||||
else
|
||||
let Json.423 : {List U8, List U8} = Struct {Json.141, Json.140};
|
||||
ret Json.423;
|
||||
else
|
||||
let Json.435 : {List U8, List U8} = Struct {Json.141, Json.140};
|
||||
ret Json.435;
|
||||
in
|
||||
jump Json.421 Json.450 Json.451;
|
||||
|
||||
procedure Json.143 (Json.432):
|
||||
let Json.433 : List U8 = StructAtIndex 1 Json.432;
|
||||
inc Json.433;
|
||||
dec Json.432;
|
||||
ret Json.433;
|
||||
|
||||
procedure Json.2 ():
|
||||
let Json.396 : {} = Struct {};
|
||||
ret Json.396;
|
||||
|
||||
procedure Json.22 (Json.137, Json.138):
|
||||
let Json.440 : List U8 = Array [];
|
||||
let Json.420 : {List U8, List U8} = Struct {Json.137, Json.440};
|
||||
let Json.419 : {List U8, List U8} = CallByName Json.139 Json.420 Json.138;
|
||||
ret Json.419;
|
||||
|
||||
procedure Json.283 (Json.284):
|
||||
let Json.442 : U8 = 34i64;
|
||||
let Json.441 : Int1 = CallByName Bool.7 Json.284 Json.442;
|
||||
ret Json.441;
|
||||
|
||||
procedure Json.293 (Json.294, Json.399):
|
||||
let Json.400 : {List U8, [C {}, C Str]} = CallByName Json.40 Json.294;
|
||||
ret Json.400;
|
||||
|
||||
procedure Json.40 (Json.276):
|
||||
let Json.446 : U64 = 1i64;
|
||||
inc Json.276;
|
||||
let Json.445 : {List U8, List U8} = CallByName List.52 Json.276 Json.446;
|
||||
let Json.277 : List U8 = StructAtIndex 0 Json.445;
|
||||
inc Json.277;
|
||||
let Json.279 : List U8 = StructAtIndex 1 Json.445;
|
||||
inc Json.279;
|
||||
dec Json.445;
|
||||
let Json.444 : U8 = 34i64;
|
||||
let Json.443 : List U8 = Array [Json.444];
|
||||
let Json.404 : Int1 = CallByName Bool.11 Json.277 Json.443;
|
||||
dec Json.443;
|
||||
dec Json.277;
|
||||
if Json.404 then
|
||||
dec Json.276;
|
||||
let Json.417 : {} = Struct {};
|
||||
let Json.416 : {List U8, List U8} = CallByName Json.22 Json.279 Json.417;
|
||||
let Json.282 : List U8 = StructAtIndex 0 Json.416;
|
||||
inc Json.282;
|
||||
let Json.281 : List U8 = StructAtIndex 1 Json.416;
|
||||
inc Json.281;
|
||||
dec Json.416;
|
||||
let Json.405 : [C {U64, U8}, C Str] = CallByName Str.9 Json.281;
|
||||
let Json.413 : U8 = 1i64;
|
||||
let Json.414 : U8 = GetTagId Json.405;
|
||||
let Json.415 : Int1 = lowlevel Eq Json.413 Json.414;
|
||||
if Json.415 then
|
||||
let Json.285 : Str = UnionAtIndex (Id 1) (Index 0) Json.405;
|
||||
inc Json.285;
|
||||
dec Json.405;
|
||||
let Json.409 : U64 = 1i64;
|
||||
let Json.408 : {List U8, List U8} = CallByName List.52 Json.282 Json.409;
|
||||
let Json.287 : List U8 = StructAtIndex 1 Json.408;
|
||||
inc Json.287;
|
||||
dec Json.408;
|
||||
let Json.407 : [C {}, C Str] = TagId(1) Json.285;
|
||||
let Json.406 : {List U8, [C {}, C Str]} = Struct {Json.287, Json.407};
|
||||
ret Json.406;
|
||||
else
|
||||
dec Json.405;
|
||||
let Json.412 : {} = Struct {};
|
||||
let Json.411 : [C {}, C Str] = TagId(0) Json.412;
|
||||
let Json.410 : {List U8, [C {}, C Str]} = Struct {Json.282, Json.411};
|
||||
ret Json.410;
|
||||
else
|
||||
dec Json.279;
|
||||
let Json.403 : {} = Struct {};
|
||||
let Json.402 : [C {}, C Str] = TagId(0) Json.403;
|
||||
let Json.401 : {List U8, [C {}, C Str]} = Struct {Json.276, Json.402};
|
||||
ret Json.401;
|
||||
|
||||
procedure Json.41 ():
|
||||
let Json.398 : {} = Struct {};
|
||||
let Json.397 : {} = CallByName Decode.23 Json.398;
|
||||
ret Json.397;
|
||||
|
||||
procedure List.1 (List.94):
|
||||
let List.479 : U64 = CallByName List.6 List.94;
|
||||
let List.480 : U64 = 0i64;
|
||||
let List.478 : Int1 = CallByName Bool.11 List.479 List.480;
|
||||
ret List.478;
|
||||
|
||||
procedure List.2 (List.95, List.96):
|
||||
let List.536 : U64 = CallByName List.6 List.95;
|
||||
let List.532 : Int1 = CallByName Num.22 List.96 List.536;
|
||||
if List.532 then
|
||||
let List.534 : U8 = CallByName List.66 List.95 List.96;
|
||||
let List.533 : [C {}, C U8] = TagId(1) List.534;
|
||||
ret List.533;
|
||||
else
|
||||
let List.531 : {} = Struct {};
|
||||
let List.530 : [C {}, C U8] = TagId(0) List.531;
|
||||
ret List.530;
|
||||
|
||||
procedure List.4 (List.106, List.107):
|
||||
let List.520 : U64 = 1i64;
|
||||
let List.518 : List U8 = CallByName List.70 List.106 List.520;
|
||||
let List.517 : List U8 = CallByName List.71 List.518 List.107;
|
||||
ret List.517;
|
||||
|
||||
procedure List.49 (List.366, List.367):
|
||||
let List.492 : U64 = StructAtIndex 0 List.367;
|
||||
let List.493 : U64 = 0i64;
|
||||
let List.490 : Int1 = CallByName Bool.11 List.492 List.493;
|
||||
if List.490 then
|
||||
dec List.366;
|
||||
let List.491 : List U8 = Array [];
|
||||
ret List.491;
|
||||
else
|
||||
let List.487 : U64 = StructAtIndex 1 List.367;
|
||||
let List.488 : U64 = StructAtIndex 0 List.367;
|
||||
let List.486 : List U8 = CallByName List.72 List.366 List.487 List.488;
|
||||
ret List.486;
|
||||
|
||||
procedure List.52 (List.381, List.382):
|
||||
let List.383 : U64 = CallByName List.6 List.381;
|
||||
joinpoint List.515 List.384:
|
||||
let List.513 : U64 = 0i64;
|
||||
let List.512 : {U64, U64} = Struct {List.384, List.513};
|
||||
inc List.381;
|
||||
let List.385 : List U8 = CallByName List.49 List.381 List.512;
|
||||
let List.511 : U64 = CallByName Num.20 List.383 List.384;
|
||||
let List.510 : {U64, U64} = Struct {List.511, List.384};
|
||||
let List.386 : List U8 = CallByName List.49 List.381 List.510;
|
||||
let List.509 : {List U8, List U8} = Struct {List.385, List.386};
|
||||
ret List.509;
|
||||
in
|
||||
let List.516 : Int1 = CallByName Num.24 List.383 List.382;
|
||||
if List.516 then
|
||||
jump List.515 List.382;
|
||||
else
|
||||
jump List.515 List.383;
|
||||
|
||||
procedure List.6 (#Attr.2):
|
||||
let List.556 : U64 = lowlevel ListLen #Attr.2;
|
||||
ret List.556;
|
||||
|
||||
procedure List.66 (#Attr.2, #Attr.3):
|
||||
let List.535 : U8 = lowlevel ListGetUnsafe #Attr.2 #Attr.3;
|
||||
ret List.535;
|
||||
|
||||
procedure List.70 (#Attr.2, #Attr.3):
|
||||
let List.521 : List U8 = lowlevel ListReserve #Attr.2 #Attr.3;
|
||||
ret List.521;
|
||||
|
||||
procedure List.71 (#Attr.2, #Attr.3):
|
||||
let List.519 : List U8 = lowlevel ListAppendUnsafe #Attr.2 #Attr.3;
|
||||
ret List.519;
|
||||
|
||||
procedure List.72 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let List.489 : List U8 = lowlevel ListSublist #Attr.2 #Attr.3 #Attr.4;
|
||||
ret List.489;
|
||||
|
||||
procedure List.9 (List.283):
|
||||
let List.529 : U64 = 0i64;
|
||||
let List.522 : [C {}, C U8] = CallByName List.2 List.283 List.529;
|
||||
let List.526 : U8 = 1i64;
|
||||
let List.527 : U8 = GetTagId List.522;
|
||||
let List.528 : Int1 = lowlevel Eq List.526 List.527;
|
||||
if List.528 then
|
||||
let List.284 : U8 = UnionAtIndex (Id 1) (Index 0) List.522;
|
||||
let List.523 : [C {}, C U8] = TagId(1) List.284;
|
||||
ret List.523;
|
||||
else
|
||||
let List.525 : {} = Struct {};
|
||||
let List.524 : [C {}, C U8] = TagId(0) List.525;
|
||||
ret List.524;
|
||||
|
||||
procedure Num.20 (#Attr.2, #Attr.3):
|
||||
let Num.258 : U64 = lowlevel NumSub #Attr.2 #Attr.3;
|
||||
ret Num.258;
|
||||
|
||||
procedure Num.22 (#Attr.2, #Attr.3):
|
||||
let Num.262 : Int1 = lowlevel NumLt #Attr.2 #Attr.3;
|
||||
ret Num.262;
|
||||
|
||||
procedure Num.24 (#Attr.2, #Attr.3):
|
||||
let Num.261 : Int1 = lowlevel NumGt #Attr.2 #Attr.3;
|
||||
ret Num.261;
|
||||
|
||||
procedure Str.48 (#Attr.2, #Attr.3, #Attr.4):
|
||||
let Str.274 : {U64, Str, Int1, U8} = lowlevel StrFromUtf8Range #Attr.2 #Attr.3 #Attr.4;
|
||||
ret Str.274;
|
||||
|
||||
procedure Str.9 (Str.76):
|
||||
let Str.272 : U64 = 0i64;
|
||||
let Str.273 : U64 = CallByName List.6 Str.76;
|
||||
let Str.77 : {U64, Str, Int1, U8} = CallByName Str.48 Str.76 Str.272 Str.273;
|
||||
let Str.269 : Int1 = StructAtIndex 2 Str.77;
|
||||
if Str.269 then
|
||||
let Str.271 : Str = StructAtIndex 1 Str.77;
|
||||
inc Str.271;
|
||||
dec Str.77;
|
||||
let Str.270 : [C {U64, U8}, C Str] = TagId(1) Str.271;
|
||||
ret Str.270;
|
||||
else
|
||||
let Str.267 : U8 = StructAtIndex 3 Str.77;
|
||||
let Str.268 : U64 = StructAtIndex 0 Str.77;
|
||||
dec Str.77;
|
||||
let Str.266 : {U64, U8} = Struct {Str.268, Str.267};
|
||||
let Str.265 : [C {U64, U8}, C Str] = TagId(0) Str.266;
|
||||
ret Str.265;
|
||||
|
||||
procedure Test.3 ():
|
||||
let Test.0 : List U8 = Array [82i64, 111i64, 99i64];
|
||||
let Test.8 : {} = CallByName Json.2;
|
||||
inc Test.0;
|
||||
let Test.1 : [C [C List U8, C ], C Str] = CallByName Decode.26 Test.0 Test.8;
|
||||
let Test.7 : Str = "Roc";
|
||||
let Test.6 : [C [C List U8, C ], C Str] = TagId(1) Test.7;
|
||||
inc Test.1;
|
||||
let Test.5 : Int1 = CallByName Bool.11 Test.1 Test.6;
|
||||
expect Test.5;
|
||||
let Test.4 : {} = Struct {};
|
||||
ret Test.4;
|
@ -0,0 +1,49 @@
|
||||
procedure Bool.11 (#Attr.2, #Attr.3):
|
||||
let Bool.23 : Int1 = lowlevel Eq #Attr.2 #Attr.3;
|
||||
ret Bool.23;
|
||||
|
||||
procedure Bool.2 ():
|
||||
let Bool.24 : Int1 = true;
|
||||
ret Bool.24;
|
||||
|
||||
procedure Num.19 (#Attr.2, #Attr.3):
|
||||
let Num.256 : U64 = lowlevel NumAdd #Attr.2 #Attr.3;
|
||||
ret Num.256;
|
||||
|
||||
procedure Num.21 (#Attr.2, #Attr.3):
|
||||
let Num.257 : U64 = lowlevel NumMul #Attr.2 #Attr.3;
|
||||
ret Num.257;
|
||||
|
||||
procedure Test.0 (Test.8):
|
||||
let Test.23 : Int1 = CallByName Bool.2;
|
||||
if Test.23 then
|
||||
let Test.24 : Int1 = true;
|
||||
ret Test.24;
|
||||
else
|
||||
let Test.22 : Int1 = false;
|
||||
ret Test.22;
|
||||
|
||||
procedure Test.5 (Test.6, Test.2):
|
||||
joinpoint Test.19 Test.18:
|
||||
ret Test.18;
|
||||
in
|
||||
switch Test.2:
|
||||
case 0:
|
||||
let Test.20 : U64 = CallByName Num.19 Test.6 Test.6;
|
||||
jump Test.19 Test.20;
|
||||
|
||||
default:
|
||||
let Test.21 : U64 = CallByName Num.21 Test.6 Test.6;
|
||||
jump Test.19 Test.21;
|
||||
|
||||
|
||||
procedure Test.7 ():
|
||||
let Test.13 : U64 = 3i64;
|
||||
let Test.15 : {} = Struct {};
|
||||
let Test.14 : Int1 = CallByName Test.0 Test.15;
|
||||
let Test.11 : U64 = CallByName Test.5 Test.13 Test.14;
|
||||
let Test.12 : U64 = 9i64;
|
||||
let Test.10 : Int1 = CallByName Bool.11 Test.11 Test.12;
|
||||
expect Test.10;
|
||||
let Test.9 : {} = Struct {};
|
||||
ret Test.9;
|
@ -2159,3 +2159,36 @@ fn issue_4705() {
|
||||
"###
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test(mode = "test")]
|
||||
fn issue_4749() {
|
||||
indoc!(
|
||||
r###"
|
||||
interface Test exposes [] imports [Json]
|
||||
|
||||
expect
|
||||
input = [82, 111, 99]
|
||||
got = Decode.fromBytes input Json.fromUtf8
|
||||
got == Ok "Roc"
|
||||
"###
|
||||
)
|
||||
}
|
||||
|
||||
#[mono_test(mode = "test", no_check)]
|
||||
fn lambda_set_with_imported_toplevels_issue_4733() {
|
||||
indoc!(
|
||||
r###"
|
||||
interface Test exposes [] imports []
|
||||
|
||||
fn = \{} ->
|
||||
instr : [ Op (U64, U64 -> U64) ]
|
||||
instr = if Bool.true then (Op Num.mul) else (Op Num.add)
|
||||
|
||||
Op op = instr
|
||||
|
||||
\a -> op a a
|
||||
|
||||
expect ((fn {}) 3) == 9
|
||||
"###
|
||||
)
|
||||
}
|
||||
|
@ -767,18 +767,23 @@ fn write_content<'a>(
|
||||
buf.push_str("[[");
|
||||
|
||||
let print_symbol = |symbol: &Symbol| {
|
||||
let ident_str = symbol.as_str(env.interns);
|
||||
let ident_index_str = symbol.ident_id().index().to_string();
|
||||
let disambiguation = if ident_str != ident_index_str {
|
||||
// The pretty name is a named identifier; print the ident as well to avoid
|
||||
// ambguity (in shadows or ability specializations).
|
||||
format!("({ident_index_str})")
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
if env.home == symbol.module_id() {
|
||||
format!(
|
||||
"{}({})",
|
||||
symbol.as_str(env.interns),
|
||||
symbol.ident_id().index(),
|
||||
)
|
||||
format!("{}{}", ident_str, disambiguation,)
|
||||
} else {
|
||||
format!(
|
||||
"{}.{}({})",
|
||||
"{}.{}{}",
|
||||
symbol.module_string(env.interns),
|
||||
symbol.as_str(env.interns),
|
||||
symbol.ident_id().index(),
|
||||
ident_str,
|
||||
disambiguation
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -3645,11 +3645,8 @@ impl Alias {
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum Mismatch {
|
||||
TypeMismatch,
|
||||
IfConditionNotBool,
|
||||
InconsistentIfElse,
|
||||
InconsistentWhenBranches,
|
||||
CanonicalizationProblem,
|
||||
TypeNotInRange,
|
||||
DisjointLambdaSets,
|
||||
DoesNotImplementAbiity(Variable, Symbol),
|
||||
}
|
||||
|
||||
|
@ -320,7 +320,6 @@ impl<M: MetaCollector> Outcome<M> {
|
||||
|
||||
pub struct Env<'a> {
|
||||
pub subs: &'a mut Subs,
|
||||
compute_outcome_only: bool,
|
||||
seen_recursion: VecSet<(Variable, Variable)>,
|
||||
fixed_variables: VecSet<Variable>,
|
||||
}
|
||||
@ -329,21 +328,11 @@ impl<'a> Env<'a> {
|
||||
pub fn new(subs: &'a mut Subs) -> Self {
|
||||
Self {
|
||||
subs,
|
||||
compute_outcome_only: false,
|
||||
seen_recursion: Default::default(),
|
||||
fixed_variables: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
// Computes a closure in outcome-only mode. Unifications run in outcome-only mode will check
|
||||
// for unifiability, but will not modify type variables or merge them.
|
||||
pub fn with_outcome_only<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> T {
|
||||
self.compute_outcome_only = true;
|
||||
let result = f(self);
|
||||
self.compute_outcome_only = false;
|
||||
result
|
||||
}
|
||||
|
||||
fn add_recursion_pair(&mut self, var1: Variable, var2: Variable) {
|
||||
let pair = (
|
||||
self.subs.get_root_key_without_compacting(var1),
|
||||
@ -1331,7 +1320,7 @@ fn separate_union_lambdas<M: MetaCollector>(
|
||||
mode: Mode,
|
||||
fields1: UnionLambdas,
|
||||
fields2: UnionLambdas,
|
||||
) -> (Outcome<M>, SeparatedUnionLambdas) {
|
||||
) -> Result<(Outcome<M>, SeparatedUnionLambdas), Outcome<M>> {
|
||||
debug_assert!(
|
||||
fields1.is_sorted_allow_duplicates(env.subs),
|
||||
"not sorted: {:?}",
|
||||
@ -1451,19 +1440,55 @@ fn separate_union_lambdas<M: MetaCollector>(
|
||||
//
|
||||
// If they are not unifiable, that means the two lambdas must be
|
||||
// different (since they have different capture sets), and so we don't
|
||||
// want to merge the variables.
|
||||
let variables_are_unifiable = env.with_outcome_only(|env| {
|
||||
unify_pool::<NoCollector>(env, pool, var1, var2, mode)
|
||||
.mismatches
|
||||
.is_empty()
|
||||
});
|
||||
// want to merge the variables. Instead, we'll treat the lambda sets
|
||||
// are disjoint, and keep them as independent lambda in the resulting
|
||||
// set.
|
||||
//
|
||||
// # Nested lambda sets
|
||||
//
|
||||
// XREF https://github.com/roc-lang/roc/issues/4712
|
||||
//
|
||||
// We must be careful to ensure that if unifying nested lambda sets
|
||||
// results in disjoint lambdas, that the parent lambda sets are
|
||||
// ultimately treated disjointly as well.
|
||||
// Consider
|
||||
//
|
||||
// v1: {} -[ foo ({} -[ bar Str ]-> {}) ]-> {}
|
||||
// ~ v2: {} -[ foo ({} -[ bar U64 ]-> {}) ]-> {}
|
||||
//
|
||||
// When considering unification of the nested sets
|
||||
//
|
||||
// [ bar Str ]
|
||||
// ~ [ bar U64 ]
|
||||
//
|
||||
// we should not unify these sets, even disjointly, because that would
|
||||
// ultimately lead us to unifying
|
||||
//
|
||||
// v1 ~ v2
|
||||
// => {} -[ foo ({} -[ bar Str, bar U64 ]-> {}) ] -> {}
|
||||
//
|
||||
// which is quite wrong - we do not have a lambda `foo` that captures
|
||||
// either `bar captures: Str` or `bar captures: U64`, we have two
|
||||
// different lambdas `foo` that capture different `bars`. The target
|
||||
// unification is
|
||||
//
|
||||
// v1 ~ v2
|
||||
// => {} -[ foo ({} -[ bar Str ]-> {}),
|
||||
// foo ({} -[ bar U64 ]-> {}) ] -> {}
|
||||
let subs_snapshot = env.subs.snapshot();
|
||||
let pool_snapshot = pool.len();
|
||||
let outcome: Outcome<M> = unify_pool(env, pool, var1, var2, mode);
|
||||
|
||||
if !variables_are_unifiable {
|
||||
if !outcome.mismatches.is_empty() {
|
||||
// Rolling back will also pull apart any nested lambdas that
|
||||
// were joined into the same set.
|
||||
env.subs.rollback_to(subs_snapshot);
|
||||
pool.truncate(pool_snapshot);
|
||||
continue 'try_next_right;
|
||||
} else {
|
||||
let outcome = unify_pool(env, pool, var1, var2, mode);
|
||||
whole_outcome.union(outcome);
|
||||
}
|
||||
|
||||
let outcome = unify_pool(env, pool, var1, var2, mode);
|
||||
whole_outcome.union(outcome);
|
||||
}
|
||||
|
||||
// All the variables unified, so we can join the left + right.
|
||||
@ -1487,14 +1512,14 @@ fn separate_union_lambdas<M: MetaCollector>(
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
Ok((
|
||||
whole_outcome,
|
||||
SeparatedUnionLambdas {
|
||||
only_in_left,
|
||||
only_in_right,
|
||||
joined,
|
||||
},
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
/// ULS-SORT-ORDER:
|
||||
@ -1829,7 +1854,10 @@ fn unify_lambda_set_help<M: MetaCollector>(
|
||||
only_in_right,
|
||||
joined,
|
||||
},
|
||||
) = separate_union_lambdas(env, pool, ctx.mode, solved1, solved2);
|
||||
) = match separate_union_lambdas(env, pool, ctx.mode, solved1, solved2) {
|
||||
Ok((outcome, separated)) => (outcome, separated),
|
||||
Err(err_outcome) => return err_outcome,
|
||||
};
|
||||
|
||||
let all_lambdas = joined
|
||||
.into_iter()
|
||||
@ -3495,24 +3523,22 @@ fn unify_recursion<M: MetaCollector>(
|
||||
pub fn merge<M: MetaCollector>(env: &mut Env, ctx: &Context, content: Content) -> Outcome<M> {
|
||||
let mut outcome: Outcome<M> = Outcome::default();
|
||||
|
||||
if !env.compute_outcome_only {
|
||||
let rank = ctx.first_desc.rank.min(ctx.second_desc.rank);
|
||||
let desc = Descriptor {
|
||||
content,
|
||||
rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
};
|
||||
let rank = ctx.first_desc.rank.min(ctx.second_desc.rank);
|
||||
let desc = Descriptor {
|
||||
content,
|
||||
rank,
|
||||
mark: Mark::NONE,
|
||||
copy: OptVariable::NONE,
|
||||
};
|
||||
|
||||
outcome
|
||||
.extra_metadata
|
||||
.record_changed_variable(env.subs, ctx.first);
|
||||
outcome
|
||||
.extra_metadata
|
||||
.record_changed_variable(env.subs, ctx.second);
|
||||
outcome
|
||||
.extra_metadata
|
||||
.record_changed_variable(env.subs, ctx.first);
|
||||
outcome
|
||||
.extra_metadata
|
||||
.record_changed_variable(env.subs, ctx.second);
|
||||
|
||||
env.subs.union(ctx.first, ctx.second, desc);
|
||||
}
|
||||
env.subs.union(ctx.first, ctx.second, desc);
|
||||
|
||||
outcome
|
||||
}
|
||||
|
@ -91,7 +91,6 @@ RocNum : [
|
||||
U128,
|
||||
F32,
|
||||
F64,
|
||||
F128,
|
||||
Dec,
|
||||
]
|
||||
|
||||
@ -153,4 +152,4 @@ RocTagUnion : [
|
||||
nonNullPayload: TypeId,
|
||||
whichTagIsNull: [FirstTagIsNull, SecondTagIsNull],
|
||||
},
|
||||
]
|
||||
]
|
||||
|
@ -571,7 +571,6 @@ struct RocType_RocDict {
|
||||
#[repr(u8)]
|
||||
pub enum RocNum {
|
||||
Dec = 0,
|
||||
F128 = 1,
|
||||
F32 = 2,
|
||||
F64 = 3,
|
||||
I128 = 4,
|
||||
@ -590,7 +589,6 @@ impl core::fmt::Debug for RocNum {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::Dec => f.write_str("RocNum::Dec"),
|
||||
Self::F128 => f.write_str("RocNum::F128"),
|
||||
Self::F32 => f.write_str("RocNum::F32"),
|
||||
Self::F64 => f.write_str("RocNum::F64"),
|
||||
Self::I128 => f.write_str("RocNum::I128"),
|
||||
|
@ -1737,7 +1737,6 @@ fn type_name(id: TypeId, types: &Types) -> String {
|
||||
RocType::Num(RocNum::I128) => "roc_std::I128".to_string(),
|
||||
RocType::Num(RocNum::F32) => "f32".to_string(),
|
||||
RocType::Num(RocNum::F64) => "f64".to_string(),
|
||||
RocType::Num(RocNum::F128) => "roc_std::F128".to_string(),
|
||||
RocType::Num(RocNum::Dec) => "roc_std::RocDec".to_string(),
|
||||
RocType::RocDict(key_id, val_id) => format!(
|
||||
"roc_std::RocDict<{}, {}>",
|
||||
@ -2433,7 +2432,7 @@ fn has_float_help(roc_type: &RocType, types: &Types, do_not_recurse: &[TypeId])
|
||||
use RocNum::*;
|
||||
|
||||
match num {
|
||||
F32 | F64 | F128 => true,
|
||||
F32 | F64 => true,
|
||||
I8 | U8 | I16 | U16 | I32 | U32 | I64 | U64 | I128 | U128 | Dec => false,
|
||||
}
|
||||
}
|
||||
|
@ -544,7 +544,6 @@ pub enum RocNum {
|
||||
U128,
|
||||
F32,
|
||||
F64,
|
||||
F128,
|
||||
Dec,
|
||||
}
|
||||
|
||||
@ -566,7 +565,6 @@ impl RocNum {
|
||||
RocNum::U128 => size_of::<roc_std::U128>(),
|
||||
RocNum::F32 => size_of::<f32>(),
|
||||
RocNum::F64 => size_of::<f64>(),
|
||||
RocNum::F128 => todo!(),
|
||||
RocNum::Dec => size_of::<roc_std::RocDec>(),
|
||||
};
|
||||
|
||||
@ -1083,11 +1081,6 @@ fn add_builtin_type<'a>(
|
||||
RocType::Num(RocNum::F64),
|
||||
layout,
|
||||
),
|
||||
F128 => types.add_anonymous(
|
||||
&env.layout_cache.interner,
|
||||
RocType::Num(RocNum::F128),
|
||||
layout,
|
||||
),
|
||||
},
|
||||
(Builtin::Decimal, _) => types.add_anonymous(
|
||||
&env.layout_cache.interner,
|
||||
|
@ -172,7 +172,7 @@ fn unroll_newtypes_and_aliases<'a, 'env>(
|
||||
var = field.into_inner();
|
||||
}
|
||||
Content::Alias(name, _, real_var, kind) => {
|
||||
if *name == Symbol::BOOL_BOOL {
|
||||
if *name == Symbol::BOOL_BOOL || name.module_id() == ModuleId::NUM {
|
||||
return (newtype_containers, alias_content, var);
|
||||
}
|
||||
// We need to pass through aliases too, because their underlying types may have
|
||||
@ -185,7 +185,7 @@ fn unroll_newtypes_and_aliases<'a, 'env>(
|
||||
//
|
||||
// At the end of the day what we should show to the user is the alias content, not
|
||||
// what's inside, so keep that around too.
|
||||
if *kind == AliasKind::Opaque && name.module_id() != ModuleId::NUM {
|
||||
if *kind == AliasKind::Opaque {
|
||||
newtype_containers.push(NewtypeKind::Opaque(*name));
|
||||
}
|
||||
alias_content = Some(content);
|
||||
@ -361,10 +361,11 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||
use Content::*;
|
||||
use IntWidth::*;
|
||||
|
||||
match (alias_content, int_width) {
|
||||
(Some(Alias(Symbol::NUM_UNSIGNED8, ..)), U8) => num_helper!(u8),
|
||||
match (env.subs.get_content_without_compacting(raw_var), int_width) {
|
||||
(Alias(Symbol::NUM_UNSIGNED8 | Symbol::NUM_U8, ..), U8) => num_helper!(u8),
|
||||
(_, U8) => {
|
||||
// This is not a number, it's a tag union or something else
|
||||
dbg!(&alias_content);
|
||||
app.call_function(main_fn_name, |_mem: &A::Memory, num: u8| {
|
||||
byte_to_ast(env, num, env.subs.get_content_without_compacting(raw_var))
|
||||
})
|
||||
@ -387,7 +388,6 @@ fn jit_to_ast_help<'a, A: ReplApp<'a>>(
|
||||
match float_width {
|
||||
F32 => num_helper!(f32),
|
||||
F64 => num_helper!(f64),
|
||||
F128 => todo!("F128 not implemented"),
|
||||
}
|
||||
}
|
||||
Layout::Builtin(Builtin::Decimal) => num_helper!(RocDec),
|
||||
@ -565,7 +565,13 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
|
||||
use IntWidth::*;
|
||||
|
||||
match int_width {
|
||||
U8 => helper!(deref_u8, u8),
|
||||
U8 => {
|
||||
if matches!(raw_content, Content::Alias(name, ..) if name.module_id() == ModuleId::NUM) {
|
||||
helper!(deref_u8, u8)
|
||||
} else {
|
||||
byte_to_ast(env, mem.deref_u8(addr), raw_content)
|
||||
}
|
||||
},
|
||||
U16 => helper!(deref_u16, u16),
|
||||
U32 => helper!(deref_u32, u32),
|
||||
U64 => helper!(deref_u64, u64),
|
||||
@ -583,7 +589,6 @@ fn addr_to_ast<'a, M: ReplAppMemory>(
|
||||
match float_width {
|
||||
F32 => helper!(deref_f32, f32),
|
||||
F64 => helper!(deref_f64, f64),
|
||||
F128 => todo!("F128 not implemented"),
|
||||
}
|
||||
}
|
||||
(_, Layout::Builtin(Builtin::List(elem_layout))) => {
|
||||
|
@ -32,9 +32,10 @@ pub fn get_values<'a>(
|
||||
layout_interner: &Arc<GlobalInterner<'a, Layout<'a>>>,
|
||||
start: *const u8,
|
||||
start_offset: usize,
|
||||
variables: &[Variable],
|
||||
) -> (usize, Vec<Expr<'a>>) {
|
||||
let mut result = Vec::with_capacity(variables.len());
|
||||
number_of_lookups: usize,
|
||||
) -> (usize, Vec<Expr<'a>>, Vec<Variable>) {
|
||||
let mut result = Vec::with_capacity(number_of_lookups);
|
||||
let mut result_vars = Vec::with_capacity(number_of_lookups);
|
||||
|
||||
let memory = ExpectMemory { start };
|
||||
|
||||
@ -45,13 +46,20 @@ pub fn get_values<'a>(
|
||||
|
||||
let app = arena.alloc(app);
|
||||
|
||||
for (i, variable) in variables.iter().enumerate() {
|
||||
let start = app.memory.deref_usize(start_offset + i * 8);
|
||||
for i in 0..number_of_lookups {
|
||||
let size_of_lookup_header = 8 /* pointer to value */ + 4 /* type variable */;
|
||||
|
||||
let start = app
|
||||
.memory
|
||||
.deref_usize(start_offset + i * size_of_lookup_header);
|
||||
let variable = app.memory.deref_u32(
|
||||
start_offset + i * size_of_lookup_header + 8, /* skip the pointer */
|
||||
);
|
||||
let variable = unsafe { Variable::from_index(variable) };
|
||||
|
||||
app.offset = start;
|
||||
|
||||
let expr = {
|
||||
let variable = *variable;
|
||||
|
||||
// TODO: pass layout_cache to jit_to_ast directly
|
||||
let mut layout_cache = LayoutCache::new(layout_interner.fork(), target_info);
|
||||
let layout = layout_cache.from_var(arena, variable, subs).unwrap();
|
||||
@ -76,9 +84,10 @@ pub fn get_values<'a>(
|
||||
};
|
||||
|
||||
result.push(expr);
|
||||
result_vars.push(variable);
|
||||
}
|
||||
|
||||
(app.offset, result)
|
||||
(app.offset, result, result_vars)
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
|
@ -25,7 +25,7 @@ use roc_mono::{ir::OptLevel, layout::Layout};
|
||||
use roc_region::all::Region;
|
||||
use roc_reporting::{error::expect::Renderer, report::RenderTarget};
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{Subs, Variable};
|
||||
use roc_types::subs::Subs;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub struct ExpectMemory<'a> {
|
||||
@ -471,7 +471,7 @@ pub fn render_dbgs_in_memory<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
fn split_expect_lookups(subs: &Subs, lookups: &[ExpectLookup]) -> (Vec<Symbol>, Vec<Variable>) {
|
||||
fn split_expect_lookups(subs: &Subs, lookups: &[ExpectLookup]) -> Vec<Symbol> {
|
||||
lookups
|
||||
.iter()
|
||||
.filter_map(
|
||||
@ -485,11 +485,11 @@ fn split_expect_lookups(subs: &Subs, lookups: &[ExpectLookup]) -> (Vec<Symbol>,
|
||||
if subs.is_function(*var) {
|
||||
None
|
||||
} else {
|
||||
Some((*symbol, *var))
|
||||
Some(*symbol)
|
||||
}
|
||||
},
|
||||
)
|
||||
.unzip()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -523,15 +523,7 @@ fn render_dbg_failure<'a>(
|
||||
|
||||
let subs = arena.alloc(&mut data.subs);
|
||||
|
||||
let current = ExpectLookup {
|
||||
symbol: current.symbol,
|
||||
var: current.var,
|
||||
ability_info: current.ability_info,
|
||||
};
|
||||
|
||||
let (_symbols, variables) = split_expect_lookups(subs, &[current]);
|
||||
|
||||
let (offset, expressions) = crate::get_values(
|
||||
let (offset, expressions, _variables) = crate::get_values(
|
||||
target_info,
|
||||
arena,
|
||||
subs,
|
||||
@ -539,7 +531,7 @@ fn render_dbg_failure<'a>(
|
||||
layout_interner,
|
||||
start,
|
||||
frame.start_offset,
|
||||
&variables,
|
||||
1,
|
||||
);
|
||||
|
||||
renderer.render_dbg(writer, &expressions, expect_region, failure_region)?;
|
||||
@ -574,24 +566,23 @@ fn render_expect_failure<'a>(
|
||||
None => panic!("region {failure_region:?} not in list of expects"),
|
||||
Some(current) => current,
|
||||
};
|
||||
let subs = arena.alloc(&mut data.subs);
|
||||
|
||||
let (symbols, variables) = split_expect_lookups(subs, current);
|
||||
let symbols = split_expect_lookups(&data.subs, current);
|
||||
|
||||
let (offset, expressions) = crate::get_values(
|
||||
let (offset, expressions, variables) = crate::get_values(
|
||||
target_info,
|
||||
arena,
|
||||
subs,
|
||||
&data.subs,
|
||||
interns,
|
||||
layout_interner,
|
||||
start,
|
||||
frame.start_offset,
|
||||
&variables,
|
||||
symbols.len(),
|
||||
);
|
||||
|
||||
renderer.render_failure(
|
||||
writer,
|
||||
subs,
|
||||
&mut data.subs,
|
||||
&symbols,
|
||||
&variables,
|
||||
&expressions,
|
||||
|
@ -1256,3 +1256,15 @@ fn newtype_by_void_is_wrapped() {
|
||||
r#"Ok 43 : Result (Num *) err"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_tag_union_in_list() {
|
||||
expect_success(
|
||||
indoc!(
|
||||
r#"
|
||||
[E, F, G, H]
|
||||
"#
|
||||
),
|
||||
r#"[E, F, G, H] : List [E, F, G, H]"#,
|
||||
);
|
||||
}
|
||||
|
@ -105,7 +105,8 @@
|
||||
debugir
|
||||
rust
|
||||
rust-bindgen
|
||||
cargo-criterion
|
||||
cargo-criterion # for benchmarks
|
||||
simple-http-server # to view roc website when trying out edits
|
||||
]);
|
||||
in {
|
||||
|
||||
|
@ -23,6 +23,9 @@
|
||||
},
|
||||
{
|
||||
"pattern": "https://web.eecs.umich.edu"
|
||||
},
|
||||
{
|
||||
"pattern": "http://0.0.0.0:8000"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
# www.roc-lang.org
|
||||
|
||||
## How to update site
|
||||
|
||||
- create a new branch, for example `update-www` based on `www`
|
||||
- pull `main` into `update-www`
|
||||
- update for example the file `www/public/index.html`
|
||||
- do a PR against `www`
|
||||
- check deploy preview
|
||||
- review and merge
|
||||
To view the website after you've made a change, execute:
|
||||
```bash
|
||||
./www/build.sh
|
||||
cd www/build
|
||||
simple-http-server # If you're using the nix flake simple-http-server will already be installed. Withouth nix you can install it with `cargo install simple-http-server`.
|
||||
```
|
||||
Open http://0.0.0.0:8000 in your browser.
|
||||
|
@ -1195,6 +1195,16 @@ expect pluralize <span class="str">"cactus"</span> <span class="str">"cacti"</sp
|
||||
</samp>
|
||||
<p>If you put this in a file named <code>main.roc</code> and run <code>roc test</code>, Roc will execute the two <code>expect</code>
|
||||
expressions (that is, the two <code>pluralize</code> calls) and report any that returned <code>false</code>.</p>
|
||||
|
||||
<p>If a test fails, it will not show the actual value that differs from the expected value.
|
||||
To show the actual value, you can write the expect like this:</p>
|
||||
|
||||
<samp>expect
|
||||
funcOut <span class="op">=</span> pluralize <span class="str">"cactus"</span> <span class="str">"cacti"</span> 1
|
||||
|
||||
funcOut <span class="op">==</span> <span class="str">"2 cactus"</span>
|
||||
</samp>
|
||||
|
||||
<h3 id="inline-expects"><a href="#inline-expects">Inline Expectations</a></h3>
|
||||
<p>For example:</p>
|
||||
<samp> <span class="kw">if</span> count <span class="op">==</span> <span class="number">1</span> then
|
||||
|
Loading…
Reference in New Issue
Block a user